summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/common.h2
-rw-r--r--src/audio_core/sink_context.h2
-rw-r--r--src/audio_core/stream.cpp7
-rw-r--r--src/audio_core/stream.h6
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/common_sizes.h43
-rw-r--r--src/common/detached_tasks.cpp2
-rw-r--r--src/common/fs/file.cpp4
-rw-r--r--src/common/fs/file.h12
-rw-r--r--src/common/fs/fs.cpp5
-rw-r--r--src/common/fs/fs.h30
-rw-r--r--src/common/hex_util.h3
-rw-r--r--src/common/literals.h31
-rw-r--r--src/common/logging/backend.cpp15
-rw-r--r--src/common/logging/types.h2
-rw-r--r--src/common/settings.cpp3
-rw-r--r--src/common/settings.h3
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp10
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp6
-rw-r--r--src/core/core.cpp5
-rw-r--r--src/core/crypto/key_manager.cpp2
-rw-r--r--src/core/file_sys/registered_cache.cpp13
-rw-r--r--src/core/file_sys/system_archive/system_version.cpp48
-rw-r--r--src/core/file_sys/vfs_real.cpp7
-rw-r--r--src/core/frontend/input.h1
-rw-r--r--src/core/hle/api_version.h40
-rw-r--r--src/core/hle/ipc_helpers.h8
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp37
-rw-r--r--src/core/hle/kernel/k_address_space_info.cpp35
-rw-r--r--src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp5
-rw-r--r--src/core/hle/kernel/k_memory_layout.h27
-rw-r--r--src/core/hle/kernel/k_page_table.cpp5
-rw-r--r--src/core/hle/kernel/k_resource_limit.cpp1
-rw-r--r--src/core/hle/kernel/k_trace.h6
-rw-r--r--src/core/hle/kernel/kernel.cpp17
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp10
-rw-r--r--src/core/hle/service/aoc/aoc_u.h1
-rw-r--r--src/core/hle/service/audio/audout_u.cpp10
-rw-r--r--src/core/hle/service/audio/hwopus.cpp45
-rw-r--r--src/core/hle/service/audio/hwopus.h4
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/npad.h4
-rw-r--r--src/core/hle/service/mii/manager.h2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp3
-rw-r--r--src/core/hle/service/service.cpp6
-rw-r--r--src/core/hle/service/spl/csrng.cpp2
-rw-r--r--src/core/hle/service/spl/module.cpp124
-rw-r--r--src/core/hle/service/spl/module.h13
-rw-r--r--src/core/hle/service/spl/spl.cpp84
-rw-r--r--src/core/hle/service/spl/spl_results.h31
-rw-r--r--src/core/hle/service/spl/spl_types.h232
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp2
-rw-r--r--src/input_common/mouse/mouse_input.cpp16
-rw-r--r--src/input_common/mouse/mouse_input.h6
-rw-r--r--src/input_common/mouse/mouse_poller.cpp2
-rw-r--r--src/input_common/touch_from_button.cpp1
-rw-r--r--src/tests/common/host_memory.cpp4
-rw-r--r--src/video_core/CMakeLists.txt1
-rw-r--r--src/video_core/buffer_cache/buffer_base.h11
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h163
-rw-r--r--src/video_core/command_classes/codecs/codec.h8
-rw-r--r--src/video_core/command_classes/vic.cpp25
-rw-r--r--src/video_core/engines/maxwell_3d.h1
-rw-r--r--src/video_core/host_shaders/astc_decoder.comp46
-rw-r--r--src/video_core/rasterizer_interface.h4
-rw-r--r--src/video_core/renderer_opengl/gl_arb_decompiler.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.h5
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h9
-rw-r--r--src/video_core/renderer_opengl/util_shaders.cpp31
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp107
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp18
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h9
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h11
-rw-r--r--src/video_core/surface.cpp7
-rw-r--r--src/video_core/surface.h2
-rw-r--r--src/video_core/texture_cache/image_base.cpp37
-rw-r--r--src/video_core/texture_cache/image_base.h12
-rw-r--r--src/video_core/texture_cache/slot_vector.h70
-rw-r--r--src/video_core/texture_cache/texture_cache.h153
-rw-r--r--src/video_core/texture_cache/util.cpp2
-rw-r--r--src/video_core/textures/astc.cpp10
-rw-r--r--src/video_core/textures/astc.h11
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp12
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h38
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp22
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h5
-rw-r--r--src/web_service/web_backend.cpp3
-rw-r--r--src/yuzu/about_dialog.cpp3
-rw-r--r--src/yuzu/bootmanager.cpp12
-rw-r--r--src/yuzu/bootmanager.h6
-rw-r--r--src/yuzu/configuration/config.cpp13
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp9
-rw-r--r--src/yuzu/configuration/configure_cpu.h1
-rw-r--r--src/yuzu/configuration/configure_cpu.ui12
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp12
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui28
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp2
-rw-r--r--src/yuzu/configuration/configure_per_game.ui7
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp4
-rw-r--r--src/yuzu/main.cpp39
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h7
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
118 files changed, 1534 insertions, 564 deletions
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
index fe546c55d..1ab537588 100644
--- a/src/audio_core/common.h
+++ b/src/audio_core/common.h
@@ -15,7 +15,7 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
15constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43}; 15constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
16} // namespace Audren 16} // namespace Audren
17 17
18constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); 18constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '9');
19constexpr std::size_t MAX_MIX_BUFFERS = 24; 19constexpr std::size_t MAX_MIX_BUFFERS = 24;
20constexpr std::size_t MAX_BIQUAD_FILTERS = 2; 20constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
21constexpr std::size_t MAX_CHANNEL_COUNT = 6; 21constexpr std::size_t MAX_CHANNEL_COUNT = 6;
diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h
index 66ee4e8a0..9e2b69785 100644
--- a/src/audio_core/sink_context.h
+++ b/src/audio_core/sink_context.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <vector>
7#include "audio_core/common.h" 9#include "audio_core/common.h"
8#include "common/common_funcs.h" 10#include "common/common_funcs.h"
9#include "common/common_types.h" 11#include "common/common_types.h"
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index ad6c587c2..5a30f55a7 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -107,9 +107,12 @@ void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
107 active_buffer = queued_buffers.front(); 107 active_buffer = queued_buffers.front();
108 queued_buffers.pop(); 108 queued_buffers.pop();
109 109
110 VolumeAdjustSamples(active_buffer->GetSamples(), game_volume); 110 auto& samples = active_buffer->GetSamples();
111 111
112 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); 112 VolumeAdjustSamples(samples, game_volume);
113
114 sink_stream.EnqueueSamples(GetNumChannels(), samples);
115 played_samples += samples.size();
113 116
114 const auto buffer_release_ns = GetBufferReleaseNS(*active_buffer); 117 const auto buffer_release_ns = GetBufferReleaseNS(*active_buffer);
115 118
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 559844b9b..dbd97ec9c 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -89,6 +89,11 @@ public:
89 return sample_rate; 89 return sample_rate;
90 } 90 }
91 91
92 /// Gets the number of samples played so far
93 [[nodiscard]] u64 GetPlayedSampleCount() const {
94 return played_samples;
95 }
96
92 /// Gets the number of channels 97 /// Gets the number of channels
93 [[nodiscard]] u32 GetNumChannels() const; 98 [[nodiscard]] u32 GetNumChannels() const;
94 99
@@ -106,6 +111,7 @@ private:
106 [[nodiscard]] std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const; 111 [[nodiscard]] std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
107 112
108 u32 sample_rate; ///< Sample rate of the stream 113 u32 sample_rate; ///< Sample rate of the stream
114 u64 played_samples{}; ///< The current played sample count
109 Format format; ///< Format of the stream 115 Format format; ///< Format of the stream
110 float game_volume = 1.0f; ///< The volume the game currently has set 116 float game_volume = 1.0f; ///< The volume the game currently has set
111 ReleaseCallback release_callback; ///< Buffer release callback for the stream 117 ReleaseCallback release_callback; ///< Buffer release callback for the stream
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 7534eb8f1..a6fa9a85d 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -110,7 +110,6 @@ add_library(common STATIC
110 cityhash.cpp 110 cityhash.cpp
111 cityhash.h 111 cityhash.h
112 common_funcs.h 112 common_funcs.h
113 common_sizes.h
114 common_types.h 113 common_types.h
115 concepts.h 114 concepts.h
116 div_ceil.h 115 div_ceil.h
@@ -134,6 +133,7 @@ add_library(common STATIC
134 host_memory.cpp 133 host_memory.cpp
135 host_memory.h 134 host_memory.h
136 intrusive_red_black_tree.h 135 intrusive_red_black_tree.h
136 literals.h
137 logging/backend.cpp 137 logging/backend.cpp
138 logging/backend.h 138 logging/backend.h
139 logging/filter.cpp 139 logging/filter.cpp
diff --git a/src/common/common_sizes.h b/src/common/common_sizes.h
deleted file mode 100644
index 7e9fd968b..000000000
--- a/src/common/common_sizes.h
+++ /dev/null
@@ -1,43 +0,0 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <limits>
8
9#include "common/common_types.h"
10
11namespace Common {
12
13enum : u64 {
14 Size_1_KB = 0x400ULL,
15 Size_64_KB = 64ULL * Size_1_KB,
16 Size_128_KB = 128ULL * Size_1_KB,
17 Size_1_MB = 0x100000ULL,
18 Size_2_MB = 2ULL * Size_1_MB,
19 Size_4_MB = 4ULL * Size_1_MB,
20 Size_5_MB = 5ULL * Size_1_MB,
21 Size_14_MB = 14ULL * Size_1_MB,
22 Size_32_MB = 32ULL * Size_1_MB,
23 Size_33_MB = 33ULL * Size_1_MB,
24 Size_128_MB = 128ULL * Size_1_MB,
25 Size_448_MB = 448ULL * Size_1_MB,
26 Size_507_MB = 507ULL * Size_1_MB,
27 Size_562_MB = 562ULL * Size_1_MB,
28 Size_1554_MB = 1554ULL * Size_1_MB,
29 Size_2048_MB = 2048ULL * Size_1_MB,
30 Size_2193_MB = 2193ULL * Size_1_MB,
31 Size_3285_MB = 3285ULL * Size_1_MB,
32 Size_4916_MB = 4916ULL * Size_1_MB,
33 Size_1_GB = 0x40000000ULL,
34 Size_2_GB = 2ULL * Size_1_GB,
35 Size_4_GB = 4ULL * Size_1_GB,
36 Size_6_GB = 6ULL * Size_1_GB,
37 Size_8_GB = 8ULL * Size_1_GB,
38 Size_64_GB = 64ULL * Size_1_GB,
39 Size_512_GB = 512ULL * Size_1_GB,
40 Size_Invalid = std::numeric_limits<u64>::max(),
41};
42
43} // namespace Common
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index f2b4939df..c1362631e 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -21,6 +21,8 @@ void DetachedTasks::WaitForAllTasks() {
21} 21}
22 22
23DetachedTasks::~DetachedTasks() { 23DetachedTasks::~DetachedTasks() {
24 WaitForAllTasks();
25
24 std::unique_lock lock{mutex}; 26 std::unique_lock lock{mutex};
25 ASSERT(count == 0); 27 ASSERT(count == 0);
26 instance = nullptr; 28 instance = nullptr;
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
index 710e88b39..077f34995 100644
--- a/src/common/fs/file.cpp
+++ b/src/common/fs/file.cpp
@@ -172,7 +172,7 @@ std::string ReadStringFromFile(const std::filesystem::path& path, FileType type)
172 172
173size_t WriteStringToFile(const std::filesystem::path& path, FileType type, 173size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
174 std::string_view string) { 174 std::string_view string) {
175 if (!IsFile(path)) { 175 if (Exists(path) && !IsFile(path)) {
176 return 0; 176 return 0;
177 } 177 }
178 178
@@ -183,7 +183,7 @@ size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
183 183
184size_t AppendStringToFile(const std::filesystem::path& path, FileType type, 184size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
185 std::string_view string) { 185 std::string_view string) {
186 if (!IsFile(path)) { 186 if (Exists(path) && !IsFile(path)) {
187 return 0; 187 return 0;
188 } 188 }
189 189
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index 0f10b6003..588fe619d 100644
--- a/src/common/fs/file.h
+++ b/src/common/fs/file.h
@@ -49,7 +49,7 @@ void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::op
49 49
50/** 50/**
51 * Reads an entire file at path and returns a string of the contents read from the file. 51 * Reads an entire file at path and returns a string of the contents read from the file.
52 * If the filesystem object at path is not a file, this function returns an empty string. 52 * If the filesystem object at path is not a regular file, this function returns an empty string.
53 * 53 *
54 * @param path Filesystem path 54 * @param path Filesystem path
55 * @param type File type 55 * @param type File type
@@ -72,7 +72,8 @@ template <typename Path>
72/** 72/**
73 * Writes a string to a file at path and returns the number of characters successfully written. 73 * Writes a string to a file at path and returns the number of characters successfully written.
74 * If a file already exists at path, its contents will be erased. 74 * If a file already exists at path, its contents will be erased.
75 * If the filesystem object at path is not a file, this function returns 0. 75 * If a file does not exist at path, it creates and opens a new empty file for writing.
76 * If the filesystem object at path exists and is not a regular file, this function returns 0.
76 * 77 *
77 * @param path Filesystem path 78 * @param path Filesystem path
78 * @param type File type 79 * @param type File type
@@ -95,7 +96,8 @@ template <typename Path>
95 96
96/** 97/**
97 * Appends a string to a file at path and returns the number of characters successfully written. 98 * Appends a string to a file at path and returns the number of characters successfully written.
98 * If the filesystem object at path is not a file, this function returns 0. 99 * If a file does not exist at path, it creates and opens a new empty file for appending.
100 * If the filesystem object at path exists and is not a regular file, this function returns 0.
99 * 101 *
100 * @param path Filesystem path 102 * @param path Filesystem path
101 * @param type File type 103 * @param type File type
@@ -394,11 +396,11 @@ public:
394 [[nodiscard]] size_t WriteString(std::span<const char> string) const; 396 [[nodiscard]] size_t WriteString(std::span<const char> string) const;
395 397
396 /** 398 /**
397 * Flushes any unwritten buffered data into the file. 399 * Attempts to flush any unwritten buffered data into the file and flush the file into the disk.
398 * 400 *
399 * @returns True if the flush was successful, false otherwise. 401 * @returns True if the flush was successful, false otherwise.
400 */ 402 */
401 [[nodiscard]] bool Flush() const; 403 bool Flush() const;
402 404
403 /** 405 /**
404 * Resizes the file to a given size. 406 * Resizes the file to a given size.
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp
index d3159e908..9089cad67 100644
--- a/src/common/fs/fs.cpp
+++ b/src/common/fs/fs.cpp
@@ -135,8 +135,9 @@ std::shared_ptr<IOFile> FileOpen(const fs::path& path, FileAccessMode mode, File
135 return nullptr; 135 return nullptr;
136 } 136 }
137 137
138 if (!IsFile(path)) { 138 if (Exists(path) && !IsFile(path)) {
139 LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file", 139 LOG_ERROR(Common_Filesystem,
140 "Filesystem object at path={} exists and is not a regular file",
140 PathToUTF8String(path)); 141 PathToUTF8String(path));
141 return nullptr; 142 return nullptr;
142 } 143 }
diff --git a/src/common/fs/fs.h b/src/common/fs/fs.h
index f6f256349..183126de3 100644
--- a/src/common/fs/fs.h
+++ b/src/common/fs/fs.h
@@ -48,18 +48,18 @@ template <typename Path>
48 * 48 *
49 * Failures occur when: 49 * Failures occur when:
50 * - Input path is not valid 50 * - Input path is not valid
51 * - Filesystem object at path is not a file 51 * - Filesystem object at path is not a regular file
52 * - Filesystem at path is read only 52 * - Filesystem at path is read only
53 * 53 *
54 * @param path Filesystem path 54 * @param path Filesystem path
55 * 55 *
56 * @returns True if file removal succeeds or file does not exist, false otherwise. 56 * @returns True if file removal succeeds or file does not exist, false otherwise.
57 */ 57 */
58[[nodiscard]] bool RemoveFile(const std::filesystem::path& path); 58bool RemoveFile(const std::filesystem::path& path);
59 59
60#ifdef _WIN32 60#ifdef _WIN32
61template <typename Path> 61template <typename Path>
62[[nodiscard]] bool RemoveFile(const Path& path) { 62bool RemoveFile(const Path& path) {
63 if constexpr (IsChar<typename Path::value_type>) { 63 if constexpr (IsChar<typename Path::value_type>) {
64 return RemoveFile(ToU8String(path)); 64 return RemoveFile(ToU8String(path));
65 } else { 65 } else {
@@ -74,7 +74,7 @@ template <typename Path>
74 * Failures occur when: 74 * Failures occur when:
75 * - One or both input path(s) is not valid 75 * - One or both input path(s) is not valid
76 * - Filesystem object at old_path does not exist 76 * - Filesystem object at old_path does not exist
77 * - Filesystem object at old_path is not a file 77 * - Filesystem object at old_path is not a regular file
78 * - Filesystem object at new_path exists 78 * - Filesystem object at new_path exists
79 * - Filesystem at either path is read only 79 * - Filesystem at either path is read only
80 * 80 *
@@ -110,8 +110,8 @@ template <typename Path1, typename Path2>
110 * 110 *
111 * Failures occur when: 111 * Failures occur when:
112 * - Input path is not valid 112 * - Input path is not valid
113 * - Filesystem object at path is not a file 113 * - Filesystem object at path exists and is not a regular file
114 * - The file is not opened 114 * - The file is not open
115 * 115 *
116 * @param path Filesystem path 116 * @param path Filesystem path
117 * @param mode File access mode 117 * @param mode File access mode
@@ -251,11 +251,11 @@ template <typename Path>
251 * 251 *
252 * @returns True if directory removal succeeds or directory does not exist, false otherwise. 252 * @returns True if directory removal succeeds or directory does not exist, false otherwise.
253 */ 253 */
254[[nodiscard]] bool RemoveDir(const std::filesystem::path& path); 254bool RemoveDir(const std::filesystem::path& path);
255 255
256#ifdef _WIN32 256#ifdef _WIN32
257template <typename Path> 257template <typename Path>
258[[nodiscard]] bool RemoveDir(const Path& path) { 258bool RemoveDir(const Path& path) {
259 if constexpr (IsChar<typename Path::value_type>) { 259 if constexpr (IsChar<typename Path::value_type>) {
260 return RemoveDir(ToU8String(path)); 260 return RemoveDir(ToU8String(path));
261 } else { 261 } else {
@@ -276,11 +276,11 @@ template <typename Path>
276 * 276 *
277 * @returns True if the directory and all of its contents are removed successfully, false otherwise. 277 * @returns True if the directory and all of its contents are removed successfully, false otherwise.
278 */ 278 */
279[[nodiscard]] bool RemoveDirRecursively(const std::filesystem::path& path); 279bool RemoveDirRecursively(const std::filesystem::path& path);
280 280
281#ifdef _WIN32 281#ifdef _WIN32
282template <typename Path> 282template <typename Path>
283[[nodiscard]] bool RemoveDirRecursively(const Path& path) { 283bool RemoveDirRecursively(const Path& path) {
284 if constexpr (IsChar<typename Path::value_type>) { 284 if constexpr (IsChar<typename Path::value_type>) {
285 return RemoveDirRecursively(ToU8String(path)); 285 return RemoveDirRecursively(ToU8String(path));
286 } else { 286 } else {
@@ -301,11 +301,11 @@ template <typename Path>
301 * 301 *
302 * @returns True if all of the directory's contents are removed successfully, false otherwise. 302 * @returns True if all of the directory's contents are removed successfully, false otherwise.
303 */ 303 */
304[[nodiscard]] bool RemoveDirContentsRecursively(const std::filesystem::path& path); 304bool RemoveDirContentsRecursively(const std::filesystem::path& path);
305 305
306#ifdef _WIN32 306#ifdef _WIN32
307template <typename Path> 307template <typename Path>
308[[nodiscard]] bool RemoveDirContentsRecursively(const Path& path) { 308bool RemoveDirContentsRecursively(const Path& path) {
309 if constexpr (IsChar<typename Path::value_type>) { 309 if constexpr (IsChar<typename Path::value_type>) {
310 return RemoveDirContentsRecursively(ToU8String(path)); 310 return RemoveDirContentsRecursively(ToU8String(path));
311 } else { 311 } else {
@@ -435,11 +435,13 @@ template <typename Path>
435#endif 435#endif
436 436
437/** 437/**
438 * Returns whether a filesystem object at path is a file. 438 * Returns whether a filesystem object at path is a regular file.
439 * A regular file is a file that stores text or binary data.
440 * It is not a directory, symlink, FIFO, socket, block device, or character device.
439 * 441 *
440 * @param path Filesystem path 442 * @param path Filesystem path
441 * 443 *
442 * @returns True if a filesystem object at path is a file, false otherwise. 444 * @returns True if a filesystem object at path is a regular file, false otherwise.
443 */ 445 */
444[[nodiscard]] bool IsFile(const std::filesystem::path& path); 446[[nodiscard]] bool IsFile(const std::filesystem::path& path);
445 447
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index a8d414fb8..f5f9e4507 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -53,8 +53,9 @@ template <typename ContiguousContainer>
53 std::string out; 53 std::string out;
54 out.reserve(std::size(data) * pad_width); 54 out.reserve(std::size(data) * pad_width);
55 55
56 const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
56 for (const u8 c : data) { 57 for (const u8 c : data) {
57 out += fmt::format(upper ? "{:02X}" : "{:02x}", c); 58 out += fmt::format(format_str, c);
58 } 59 }
59 60
60 return out; 61 return out;
diff --git a/src/common/literals.h b/src/common/literals.h
new file mode 100644
index 000000000..d55fed40b
--- /dev/null
+++ b/src/common/literals.h
@@ -0,0 +1,31 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Common::Literals {
10
11constexpr u64 operator""_KiB(unsigned long long int x) {
12 return 1024ULL * x;
13}
14
15constexpr u64 operator""_MiB(unsigned long long int x) {
16 return 1024_KiB * x;
17}
18
19constexpr u64 operator""_GiB(unsigned long long int x) {
20 return 1024_MiB * x;
21}
22
23constexpr u64 operator""_TiB(unsigned long long int x) {
24 return 1024_GiB * x;
25}
26
27constexpr u64 operator""_PiB(unsigned long long int x) {
28 return 1024_TiB * x;
29}
30
31} // namespace Common::Literals
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index d5cff400f..b6fa4affb 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -19,6 +19,8 @@
19#include "common/assert.h" 19#include "common/assert.h"
20#include "common/fs/file.h" 20#include "common/fs/file.h"
21#include "common/fs/fs.h" 21#include "common/fs/fs.h"
22#include "common/literals.h"
23
22#include "common/logging/backend.h" 24#include "common/logging/backend.h"
23#include "common/logging/log.h" 25#include "common/logging/log.h"
24#include "common/logging/text_formatter.h" 26#include "common/logging/text_formatter.h"
@@ -98,8 +100,8 @@ private:
98 write_logs(entry); 100 write_logs(entry);
99 } 101 }
100 102
101 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case 103 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
102 // where a system is repeatedly spamming logs even on close. 104 // case where a system is repeatedly spamming logs even on close.
103 const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100; 105 const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
104 int logs_written = 0; 106 int logs_written = 0;
105 while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) { 107 while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
@@ -159,7 +161,7 @@ FileBackend::FileBackend(const std::filesystem::path& filename) {
159 161
160 // Existence checks are done within the functions themselves. 162 // Existence checks are done within the functions themselves.
161 // We don't particularly care if these succeed or not. 163 // We don't particularly care if these succeed or not.
162 void(FS::RemoveFile(old_filename)); 164 FS::RemoveFile(old_filename);
163 void(FS::RenameFile(filename, old_filename)); 165 void(FS::RenameFile(filename, old_filename));
164 166
165 file = 167 file =
@@ -169,10 +171,11 @@ FileBackend::FileBackend(const std::filesystem::path& filename) {
169FileBackend::~FileBackend() = default; 171FileBackend::~FileBackend() = default;
170 172
171void FileBackend::Write(const Entry& entry) { 173void FileBackend::Write(const Entry& entry) {
174 using namespace Common::Literals;
172 // prevent logs from going over the maximum size (in case its spamming and the user doesn't 175 // prevent logs from going over the maximum size (in case its spamming and the user doesn't
173 // know) 176 // know)
174 constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024; 177 constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
175 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024; 178 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
176 179
177 if (!file->IsOpen()) { 180 if (!file->IsOpen()) {
178 return; 181 return;
@@ -186,7 +189,7 @@ void FileBackend::Write(const Entry& entry) {
186 189
187 bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); 190 bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
188 if (entry.log_level >= Level::Error) { 191 if (entry.log_level >= Level::Error) {
189 void(file->Flush()); 192 file->Flush();
190 } 193 }
191} 194}
192 195
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index ee9a1ed84..88b0e9c01 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once
6
5#include <chrono> 7#include <chrono>
6 8
7#include "common/common_types.h" 9#include "common/common_types.h"
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 9ec71eced..e1bb4b7ff 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -59,6 +59,7 @@ void LogSettings() {
59 log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); 59 log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
60 log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue()); 60 log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
61 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); 61 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
62 log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue());
62 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); 63 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
63 log_setting("Audio_OutputEngine", values.sink_id); 64 log_setting("Audio_OutputEngine", values.sink_id);
64 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); 65 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
@@ -122,6 +123,7 @@ void RestoreGlobalState(bool is_powered_on) {
122 values.cpu_accuracy.SetGlobal(true); 123 values.cpu_accuracy.SetGlobal(true);
123 values.cpuopt_unsafe_unfuse_fma.SetGlobal(true); 124 values.cpuopt_unsafe_unfuse_fma.SetGlobal(true);
124 values.cpuopt_unsafe_reduce_fp_error.SetGlobal(true); 125 values.cpuopt_unsafe_reduce_fp_error.SetGlobal(true);
126 values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true);
125 values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true); 127 values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
126 values.cpuopt_unsafe_fastmem_check.SetGlobal(true); 128 values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
127 129
@@ -141,6 +143,7 @@ void RestoreGlobalState(bool is_powered_on) {
141 values.use_assembly_shaders.SetGlobal(true); 143 values.use_assembly_shaders.SetGlobal(true);
142 values.use_asynchronous_shaders.SetGlobal(true); 144 values.use_asynchronous_shaders.SetGlobal(true);
143 values.use_fast_gpu_time.SetGlobal(true); 145 values.use_fast_gpu_time.SetGlobal(true);
146 values.use_caches_gc.SetGlobal(true);
144 values.bg_red.SetGlobal(true); 147 values.bg_red.SetGlobal(true);
145 values.bg_green.SetGlobal(true); 148 values.bg_green.SetGlobal(true);
146 values.bg_blue.SetGlobal(true); 149 values.bg_blue.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 6198f2d9f..82ec18e27 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -129,6 +129,7 @@ struct Values {
129 129
130 Setting<bool> cpuopt_unsafe_unfuse_fma; 130 Setting<bool> cpuopt_unsafe_unfuse_fma;
131 Setting<bool> cpuopt_unsafe_reduce_fp_error; 131 Setting<bool> cpuopt_unsafe_reduce_fp_error;
132 Setting<bool> cpuopt_unsafe_ignore_standard_fpcr;
132 Setting<bool> cpuopt_unsafe_inaccurate_nan; 133 Setting<bool> cpuopt_unsafe_inaccurate_nan;
133 Setting<bool> cpuopt_unsafe_fastmem_check; 134 Setting<bool> cpuopt_unsafe_fastmem_check;
134 135
@@ -149,9 +150,11 @@ struct Values {
149 Setting<bool> use_nvdec_emulation; 150 Setting<bool> use_nvdec_emulation;
150 Setting<bool> accelerate_astc; 151 Setting<bool> accelerate_astc;
151 Setting<bool> use_vsync; 152 Setting<bool> use_vsync;
153 Setting<bool> disable_fps_limit;
152 Setting<bool> use_assembly_shaders; 154 Setting<bool> use_assembly_shaders;
153 Setting<bool> use_asynchronous_shaders; 155 Setting<bool> use_asynchronous_shaders;
154 Setting<bool> use_fast_gpu_time; 156 Setting<bool> use_fast_gpu_time;
157 Setting<bool> use_caches_gc;
155 158
156 Setting<float> bg_red; 159 Setting<float> bg_red;
157 Setting<float> bg_green; 160 Setting<float> bg_green;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index efb851f5a..83b5b7676 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -139,6 +139,7 @@ add_library(core STATIC
139 frontend/input.h 139 frontend/input.h
140 hardware_interrupt_manager.cpp 140 hardware_interrupt_manager.cpp
141 hardware_interrupt_manager.h 141 hardware_interrupt_manager.h
142 hle/api_version.h
142 hle/ipc.h 143 hle/ipc.h
143 hle/ipc_helpers.h 144 hle/ipc_helpers.h
144 hle/kernel/board/nintendo/nx/k_system_control.cpp 145 hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -550,6 +551,8 @@ add_library(core STATIC
550 hle/service/spl/module.h 551 hle/service/spl/module.h
551 hle/service/spl/spl.cpp 552 hle/service/spl/spl.cpp
552 hle/service/spl/spl.h 553 hle/service/spl/spl.h
554 hle/service/spl/spl_results.h
555 hle/service/spl/spl_types.h
553 hle/service/ssl/ssl.cpp 556 hle/service/ssl/ssl.cpp
554 hle/service/ssl/ssl.h 557 hle/service/ssl/ssl.h
555 hle/service/time/clock_types.h 558 hle/service/time/clock_types.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index c8f6dc765..77a44f862 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -8,6 +8,7 @@
8#include <dynarmic/interface/A32/config.h> 8#include <dynarmic/interface/A32/config.h>
9#include <dynarmic/interface/A32/context.h> 9#include <dynarmic/interface/A32/context.h>
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/literals.h"
11#include "common/logging/log.h" 12#include "common/logging/log.h"
12#include "common/page_table.h" 13#include "common/page_table.h"
13#include "common/settings.h" 14#include "common/settings.h"
@@ -22,6 +23,8 @@
22 23
23namespace Core { 24namespace Core {
24 25
26using namespace Common::Literals;
27
25class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 28class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
26public: 29public:
27 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) 30 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -143,8 +146,8 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
143 config.wall_clock_cntpct = uses_wall_clock; 146 config.wall_clock_cntpct = uses_wall_clock;
144 147
145 // Code cache size 148 // Code cache size
146 config.code_cache_size = 512 * 1024 * 1024; 149 config.code_cache_size = 512_MiB;
147 config.far_code_offset = 400 * 1024 * 1024; 150 config.far_code_offset = 400_MiB;
148 151
149 // Safe optimizations 152 // Safe optimizations
150 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) { 153 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) {
@@ -186,6 +189,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
186 if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) { 189 if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) {
187 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; 190 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
188 } 191 }
192 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr.GetValue()) {
193 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
194 }
189 if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) { 195 if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) {
190 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 196 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
191 } 197 }
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index ba524cd05..75332e348 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -7,6 +7,7 @@
7#include <dynarmic/interface/A64/a64.h> 7#include <dynarmic/interface/A64/a64.h>
8#include <dynarmic/interface/A64/config.h> 8#include <dynarmic/interface/A64/config.h>
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/literals.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "common/page_table.h" 12#include "common/page_table.h"
12#include "common/settings.h" 13#include "common/settings.h"
@@ -24,6 +25,7 @@
24namespace Core { 25namespace Core {
25 26
26using Vector = Dynarmic::A64::Vector; 27using Vector = Dynarmic::A64::Vector;
28using namespace Common::Literals;
27 29
28class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 30class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
29public: 31public:
@@ -184,8 +186,8 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
184 config.wall_clock_cntpct = uses_wall_clock; 186 config.wall_clock_cntpct = uses_wall_clock;
185 187
186 // Code cache size 188 // Code cache size
187 config.code_cache_size = 512 * 1024 * 1024; 189 config.code_cache_size = 512_MiB;
188 config.far_code_offset = 400 * 1024 * 1024; 190 config.far_code_offset = 400_MiB;
189 191
190 // Safe optimizations 192 // Safe optimizations
191 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) { 193 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) {
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c5004b7b4..e6f1aa0e7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array> 5#include <array>
6#include <atomic>
6#include <memory> 7#include <memory>
7#include <utility> 8#include <utility>
8 9
@@ -377,7 +378,7 @@ struct System::Impl {
377 std::unique_ptr<Core::DeviceMemory> device_memory; 378 std::unique_ptr<Core::DeviceMemory> device_memory;
378 Core::Memory::Memory memory; 379 Core::Memory::Memory memory;
379 CpuManager cpu_manager; 380 CpuManager cpu_manager;
380 bool is_powered_on = false; 381 std::atomic_bool is_powered_on{};
381 bool exit_lock = false; 382 bool exit_lock = false;
382 383
383 Reporter reporter; 384 Reporter reporter;
@@ -463,7 +464,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
463} 464}
464 465
465bool System::IsPoweredOn() const { 466bool System::IsPoweredOn() const {
466 return impl->is_powered_on; 467 return impl->is_powered_on.load(std::memory_order::relaxed);
467} 468}
468 469
469void System::PrepareReschedule() { 470void System::PrepareReschedule() {
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index fb451a423..a98daed89 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -835,7 +835,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
835 "key_area_key_ocean_{:02X}", 835 "key_area_key_ocean_{:02X}",
836 "key_area_key_system_{:02X}", 836 "key_area_key_system_{:02X}",
837 }; 837 };
838 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key); 838 WriteKeyToFile(category, fmt::format(fmt::runtime(kak_names.at(field2)), field1), key);
839 } else if (id == S128KeyType::Master) { 839 } else if (id == S128KeyType::Master) {
840 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key); 840 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
841 } else if (id == S128KeyType::Package1) { 841 } else if (id == S128KeyType::Package1) {
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 066c6789a..7a646b5f1 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -58,14 +58,17 @@ static bool FollowsNcaIdFormat(std::string_view name) {
58 58
59static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper, 59static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
60 bool within_two_digit, bool cnmt_suffix) { 60 bool within_two_digit, bool cnmt_suffix) {
61 if (!within_two_digit) 61 if (!within_two_digit) {
62 return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca", 62 const auto format_str = fmt::runtime(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca");
63 Common::HexToString(nca_id, second_hex_upper)); 63 return fmt::format(format_str, Common::HexToString(nca_id, second_hex_upper));
64 }
64 65
65 Core::Crypto::SHA256Hash hash{}; 66 Core::Crypto::SHA256Hash hash{};
66 mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0); 67 mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
67 return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0], 68
68 Common::HexToString(nca_id, second_hex_upper)); 69 const auto format_str =
70 fmt::runtime(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca");
71 return fmt::format(format_str, hash[0], Common::HexToString(nca_id, second_hex_upper));
69} 72}
70 73
71static std::string GetCNMTName(TitleType type, u64 title_id) { 74static std::string GetCNMTName(TitleType type, u64 title_id) {
diff --git a/src/core/file_sys/system_archive/system_version.cpp b/src/core/file_sys/system_archive/system_version.cpp
index 54704105b..9b76d007e 100644
--- a/src/core/file_sys/system_archive/system_version.cpp
+++ b/src/core/file_sys/system_archive/system_version.cpp
@@ -4,47 +4,29 @@
4 4
5#include "core/file_sys/system_archive/system_version.h" 5#include "core/file_sys/system_archive/system_version.h"
6#include "core/file_sys/vfs_vector.h" 6#include "core/file_sys/vfs_vector.h"
7#include "core/hle/api_version.h"
7 8
8namespace FileSys::SystemArchive { 9namespace FileSys::SystemArchive {
9 10
10namespace SystemVersionData {
11
12// This section should reflect the best system version to describe yuzu's HLE api.
13// TODO(DarkLordZach): Update when HLE gets better.
14
15constexpr u8 VERSION_MAJOR = 11;
16constexpr u8 VERSION_MINOR = 0;
17constexpr u8 VERSION_MICRO = 1;
18
19constexpr u8 REVISION_MAJOR = 1;
20constexpr u8 REVISION_MINOR = 0;
21
22constexpr char PLATFORM_STRING[] = "NX";
23constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
24constexpr char DISPLAY_VERSION[] = "11.0.1";
25constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
26
27} // namespace SystemVersionData
28
29std::string GetLongDisplayVersion() { 11std::string GetLongDisplayVersion() {
30 return SystemVersionData::DISPLAY_TITLE; 12 return HLE::ApiVersion::DISPLAY_TITLE;
31} 13}
32 14
33VirtualDir SystemVersion() { 15VirtualDir SystemVersion() {
34 VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file"); 16 VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
35 file->WriteObject(SystemVersionData::VERSION_MAJOR, 0); 17 file->WriteObject(HLE::ApiVersion::HOS_VERSION_MAJOR, 0);
36 file->WriteObject(SystemVersionData::VERSION_MINOR, 1); 18 file->WriteObject(HLE::ApiVersion::HOS_VERSION_MINOR, 1);
37 file->WriteObject(SystemVersionData::VERSION_MICRO, 2); 19 file->WriteObject(HLE::ApiVersion::HOS_VERSION_MICRO, 2);
38 file->WriteObject(SystemVersionData::REVISION_MAJOR, 4); 20 file->WriteObject(HLE::ApiVersion::SDK_REVISION_MAJOR, 4);
39 file->WriteObject(SystemVersionData::REVISION_MINOR, 5); 21 file->WriteObject(HLE::ApiVersion::SDK_REVISION_MINOR, 5);
40 file->WriteArray(SystemVersionData::PLATFORM_STRING, 22 file->WriteArray(HLE::ApiVersion::PLATFORM_STRING,
41 std::min<u64>(sizeof(SystemVersionData::PLATFORM_STRING), 0x20ULL), 0x8); 23 std::min<u64>(sizeof(HLE::ApiVersion::PLATFORM_STRING), 0x20ULL), 0x8);
42 file->WriteArray(SystemVersionData::VERSION_HASH, 24 file->WriteArray(HLE::ApiVersion::VERSION_HASH,
43 std::min<u64>(sizeof(SystemVersionData::VERSION_HASH), 0x40ULL), 0x28); 25 std::min<u64>(sizeof(HLE::ApiVersion::VERSION_HASH), 0x40ULL), 0x28);
44 file->WriteArray(SystemVersionData::DISPLAY_VERSION, 26 file->WriteArray(HLE::ApiVersion::DISPLAY_VERSION,
45 std::min<u64>(sizeof(SystemVersionData::DISPLAY_VERSION), 0x18ULL), 0x68); 27 std::min<u64>(sizeof(HLE::ApiVersion::DISPLAY_VERSION), 0x18ULL), 0x68);
46 file->WriteArray(SystemVersionData::DISPLAY_TITLE, 28 file->WriteArray(HLE::ApiVersion::DISPLAY_TITLE,
47 std::min<u64>(sizeof(SystemVersionData::DISPLAY_TITLE), 0x80ULL), 0x80); 29 std::min<u64>(sizeof(HLE::ApiVersion::DISPLAY_TITLE), 0x80ULL), 0x80);
48 return std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{file}, 30 return std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{file},
49 std::vector<VirtualDir>{}, "data"); 31 std::vector<VirtualDir>{}, "data");
50} 32}
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index d0b8fd046..3dad54f49 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -24,17 +24,12 @@ constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) {
24 case Mode::Read: 24 case Mode::Read:
25 return FS::FileAccessMode::Read; 25 return FS::FileAccessMode::Read;
26 case Mode::Write: 26 case Mode::Write:
27 return FS::FileAccessMode::Write;
28 case Mode::ReadWrite: 27 case Mode::ReadWrite:
29 return FS::FileAccessMode::ReadWrite;
30 case Mode::Append: 28 case Mode::Append:
31 return FS::FileAccessMode::Append;
32 case Mode::ReadAppend: 29 case Mode::ReadAppend:
33 return FS::FileAccessMode::ReadAppend;
34 case Mode::WriteAppend: 30 case Mode::WriteAppend:
35 return FS::FileAccessMode::Append;
36 case Mode::All: 31 case Mode::All:
37 return FS::FileAccessMode::ReadAppend; 32 return FS::FileAccessMode::ReadWrite;
38 default: 33 default:
39 return {}; 34 return {};
40 } 35 }
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 7a047803e..f1747c5b2 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
7#include <memory> 8#include <memory>
8#include <string> 9#include <string>
9#include <tuple> 10#include <tuple>
diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h
new file mode 100644
index 000000000..5e10a7ad9
--- /dev/null
+++ b/src/core/hle/api_version.h
@@ -0,0 +1,40 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9// This file contains yuzu's HLE API version constants.
10
11namespace HLE::ApiVersion {
12
13// Horizon OS version constants.
14
15constexpr u8 HOS_VERSION_MAJOR = 11;
16constexpr u8 HOS_VERSION_MINOR = 0;
17constexpr u8 HOS_VERSION_MICRO = 1;
18
19// NintendoSDK version constants.
20
21constexpr u8 SDK_REVISION_MAJOR = 1;
22constexpr u8 SDK_REVISION_MINOR = 0;
23
24constexpr char PLATFORM_STRING[] = "NX";
25constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
26constexpr char DISPLAY_VERSION[] = "11.0.1";
27constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
28
29// Atmosphere version constants.
30
31constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0;
32constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19;
33constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 4;
34
35constexpr u32 GetTargetFirmware() {
36 return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 |
37 u32{HOS_VERSION_MICRO} << 8 | 0U;
38}
39
40} // namespace HLE::ApiVersion
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 61bda3786..ceff2532d 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -345,8 +345,12 @@ public:
345 explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {} 345 explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
346 346
347 explicit RequestParser(Kernel::HLERequestContext& ctx) : RequestHelperBase(ctx) { 347 explicit RequestParser(Kernel::HLERequestContext& ctx) : RequestHelperBase(ctx) {
348 ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete"); 348 // TIPC does not have data payload offset
349 Skip(ctx.GetDataPayloadOffset(), false); 349 if (!ctx.IsTipc()) {
350 ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete");
351 Skip(ctx.GetDataPayloadOffset(), false);
352 }
353
350 // Skip the u64 command id, it's already stored in the context 354 // Skip the u64 command id, it's already stored in the context
351 static constexpr u32 CommandIdSize = 2; 355 static constexpr u32 CommandIdSize = 2;
352 Skip(CommandIdSize, false); 356 Skip(CommandIdSize, false);
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 86472b5ce..6f335c251 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -4,7 +4,8 @@
4 4
5#include <random> 5#include <random>
6 6
7#include "common/common_sizes.h" 7#include "common/literals.h"
8
8#include "core/hle/kernel/board/nintendo/nx/k_system_control.h" 9#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
9#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h" 10#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
10#include "core/hle/kernel/k_trace.h" 11#include "core/hle/kernel/k_trace.h"
@@ -25,6 +26,8 @@ constexpr const std::size_t RequiredNonSecureSystemMemorySize =
25 26
26namespace { 27namespace {
27 28
29using namespace Common::Literals;
30
28u32 GetMemoryModeForInit() { 31u32 GetMemoryModeForInit() {
29 return 0x01; 32 return 0x01;
30} 33}
@@ -57,11 +60,11 @@ size_t KSystemControl::Init::GetIntendedMemorySize() {
57 switch (GetMemorySizeForInit()) { 60 switch (GetMemorySizeForInit()) {
58 case Smc::MemorySize_4GB: 61 case Smc::MemorySize_4GB:
59 default: // All invalid modes should go to 4GB. 62 default: // All invalid modes should go to 4GB.
60 return Common::Size_4_GB; 63 return 4_GiB;
61 case Smc::MemorySize_6GB: 64 case Smc::MemorySize_6GB:
62 return Common::Size_6_GB; 65 return 6_GiB;
63 case Smc::MemorySize_8GB: 66 case Smc::MemorySize_8GB:
64 return Common::Size_8_GB; 67 return 8_GiB;
65 } 68 }
66} 69}
67 70
@@ -79,17 +82,17 @@ std::size_t KSystemControl::Init::GetApplicationPoolSize() {
79 switch (GetMemoryArrangeForInit()) { 82 switch (GetMemoryArrangeForInit()) {
80 case Smc::MemoryArrangement_4GB: 83 case Smc::MemoryArrangement_4GB:
81 default: 84 default:
82 return Common::Size_3285_MB; 85 return 3285_MiB;
83 case Smc::MemoryArrangement_4GBForAppletDev: 86 case Smc::MemoryArrangement_4GBForAppletDev:
84 return Common::Size_2048_MB; 87 return 2048_MiB;
85 case Smc::MemoryArrangement_4GBForSystemDev: 88 case Smc::MemoryArrangement_4GBForSystemDev:
86 return Common::Size_3285_MB; 89 return 3285_MiB;
87 case Smc::MemoryArrangement_6GB: 90 case Smc::MemoryArrangement_6GB:
88 return Common::Size_4916_MB; 91 return 4916_MiB;
89 case Smc::MemoryArrangement_6GBForAppletDev: 92 case Smc::MemoryArrangement_6GBForAppletDev:
90 return Common::Size_3285_MB; 93 return 3285_MiB;
91 case Smc::MemoryArrangement_8GB: 94 case Smc::MemoryArrangement_8GB:
92 return Common::Size_4916_MB; 95 return 4916_MiB;
93 } 96 }
94 }(); 97 }();
95 98
@@ -103,22 +106,22 @@ size_t KSystemControl::Init::GetAppletPoolSize() {
103 switch (GetMemoryArrangeForInit()) { 106 switch (GetMemoryArrangeForInit()) {
104 case Smc::MemoryArrangement_4GB: 107 case Smc::MemoryArrangement_4GB:
105 default: 108 default:
106 return Common::Size_507_MB; 109 return 507_MiB;
107 case Smc::MemoryArrangement_4GBForAppletDev: 110 case Smc::MemoryArrangement_4GBForAppletDev:
108 return Common::Size_1554_MB; 111 return 1554_MiB;
109 case Smc::MemoryArrangement_4GBForSystemDev: 112 case Smc::MemoryArrangement_4GBForSystemDev:
110 return Common::Size_448_MB; 113 return 448_MiB;
111 case Smc::MemoryArrangement_6GB: 114 case Smc::MemoryArrangement_6GB:
112 return Common::Size_562_MB; 115 return 562_MiB;
113 case Smc::MemoryArrangement_6GBForAppletDev: 116 case Smc::MemoryArrangement_6GBForAppletDev:
114 return Common::Size_2193_MB; 117 return 2193_MiB;
115 case Smc::MemoryArrangement_8GB: 118 case Smc::MemoryArrangement_8GB:
116 return Common::Size_2193_MB; 119 return 2193_MiB;
117 } 120 }
118 }(); 121 }();
119 122
120 // Return (possibly) adjusted size. 123 // Return (possibly) adjusted size.
121 constexpr size_t ExtraSystemMemoryForAtmosphere = Common::Size_33_MB; 124 constexpr size_t ExtraSystemMemoryForAtmosphere = 33_MiB;
122 return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize; 125 return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
123} 126}
124 127
diff --git a/src/core/hle/kernel/k_address_space_info.cpp b/src/core/hle/kernel/k_address_space_info.cpp
index c7549f7a2..ca29edc88 100644
--- a/src/core/hle/kernel/k_address_space_info.cpp
+++ b/src/core/hle/kernel/k_address_space_info.cpp
@@ -5,34 +5,37 @@
5#include <array> 5#include <array>
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_sizes.h" 8#include "common/literals.h"
9#include "core/hle/kernel/k_address_space_info.h" 9#include "core/hle/kernel/k_address_space_info.h"
10 10
11namespace Kernel { 11namespace Kernel {
12 12
13namespace { 13namespace {
14 14
15using namespace Common::Literals;
16
17constexpr u64 Size_Invalid = UINT64_MAX;
18
15// clang-format off 19// clang-format off
16constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{ 20constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
17 { .bit_width = 32, .address = Common::Size_2_MB , .size = Common::Size_1_GB - Common::Size_2_MB , .type = KAddressSpaceInfo::Type::MapSmall, }, 21 { .bit_width = 32, .address = 2_MiB , .size = 1_GiB - 2_MiB , .type = KAddressSpaceInfo::Type::MapSmall, },
18 { .bit_width = 32, .address = Common::Size_1_GB , .size = Common::Size_4_GB - Common::Size_1_GB , .type = KAddressSpaceInfo::Type::MapLarge, }, 22 { .bit_width = 32, .address = 1_GiB , .size = 4_GiB - 1_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
19 { .bit_width = 32, .address = Common::Size_Invalid, .size = Common::Size_1_GB , .type = KAddressSpaceInfo::Type::Alias, }, 23 { .bit_width = 32, .address = Size_Invalid, .size = 1_GiB , .type = KAddressSpaceInfo::Type::Alias, },
20 { .bit_width = 32, .address = Common::Size_Invalid, .size = Common::Size_1_GB , .type = KAddressSpaceInfo::Type::Heap, }, 24 { .bit_width = 32, .address = Size_Invalid, .size = 1_GiB , .type = KAddressSpaceInfo::Type::Heap, },
21 { .bit_width = 36, .address = Common::Size_128_MB , .size = Common::Size_2_GB - Common::Size_128_MB, .type = KAddressSpaceInfo::Type::MapSmall, }, 25 { .bit_width = 36, .address = 128_MiB , .size = 2_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::MapSmall, },
22 { .bit_width = 36, .address = Common::Size_2_GB , .size = Common::Size_64_GB - Common::Size_2_GB , .type = KAddressSpaceInfo::Type::MapLarge, }, 26 { .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
23 { .bit_width = 36, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, }, 27 { .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Heap, },
24 { .bit_width = 36, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Alias, }, 28 { .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, },
25 { .bit_width = 39, .address = Common::Size_128_MB , .size = Common::Size_512_GB - Common::Size_128_MB, .type = KAddressSpaceInfo::Type::Map39Bit, }, 29 { .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
26 { .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_64_GB , .type = KAddressSpaceInfo::Type::MapSmall }, 30 { .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::MapSmall },
27 { .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, }, 31 { .bit_width = 39, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Heap, },
28 { .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_64_GB , .type = KAddressSpaceInfo::Type::Alias, }, 32 { .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::Alias, },
29 { .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_2_GB , .type = KAddressSpaceInfo::Type::Stack, }, 33 { .bit_width = 39, .address = Size_Invalid, .size = 2_GiB , .type = KAddressSpaceInfo::Type::Stack, },
30}}; 34}};
31// clang-format on 35// clang-format on
32 36
33constexpr bool IsAllowedIndexForAddress(std::size_t index) { 37constexpr bool IsAllowedIndexForAddress(std::size_t index) {
34 return index < AddressSpaceInfos.size() && 38 return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Size_Invalid;
35 AddressSpaceInfos[index].address != Common::Size_Invalid;
36} 39}
37 40
38using IndexArray = 41using IndexArray =
diff --git a/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp b/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp
index a78551291..af652af58 100644
--- a/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp
+++ b/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/alignment.h" 5#include "common/alignment.h"
6#include "common/literals.h"
6#include "core/hle/kernel/k_memory_layout.h" 7#include "core/hle/kernel/k_memory_layout.h"
7#include "core/hle/kernel/k_memory_manager.h" 8#include "core/hle/kernel/k_memory_manager.h"
8#include "core/hle/kernel/k_system_control.h" 9#include "core/hle/kernel/k_system_control.h"
@@ -12,8 +13,10 @@ namespace Kernel {
12 13
13namespace { 14namespace {
14 15
16using namespace Common::Literals;
17
15constexpr size_t CarveoutAlignment = 0x20000; 18constexpr size_t CarveoutAlignment = 0x20000;
16constexpr size_t CarveoutSizeMax = (512ULL * 1024 * 1024) - CarveoutAlignment; 19constexpr size_t CarveoutSizeMax = (512_MiB) - CarveoutAlignment;
17 20
18bool SetupPowerManagementControllerMemoryRegion(KMemoryLayout& memory_layout) { 21bool SetupPowerManagementControllerMemoryRegion(KMemoryLayout& memory_layout) {
19 // Above firmware 2.0.0, the PMC is not mappable. 22 // Above firmware 2.0.0, the PMC is not mappable.
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index 288642d9a..57ff538cc 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -7,8 +7,7 @@
7#include <utility> 7#include <utility>
8 8
9#include "common/alignment.h" 9#include "common/alignment.h"
10#include "common/common_sizes.h" 10#include "common/literals.h"
11#include "common/common_types.h"
12#include "core/device_memory.h" 11#include "core/device_memory.h"
13#include "core/hle/kernel/k_memory_region.h" 12#include "core/hle/kernel/k_memory_region.h"
14#include "core/hle/kernel/k_memory_region_type.h" 13#include "core/hle/kernel/k_memory_region_type.h"
@@ -16,20 +15,22 @@
16 15
17namespace Kernel { 16namespace Kernel {
18 17
19constexpr std::size_t L1BlockSize = Common::Size_1_GB; 18using namespace Common::Literals;
20constexpr std::size_t L2BlockSize = Common::Size_2_MB; 19
20constexpr std::size_t L1BlockSize = 1_GiB;
21constexpr std::size_t L2BlockSize = 2_MiB;
21 22
22constexpr std::size_t GetMaximumOverheadSize(std::size_t size) { 23constexpr std::size_t GetMaximumOverheadSize(std::size_t size) {
23 return (Common::DivideUp(size, L1BlockSize) + Common::DivideUp(size, L2BlockSize)) * PageSize; 24 return (Common::DivideUp(size, L1BlockSize) + Common::DivideUp(size, L2BlockSize)) * PageSize;
24} 25}
25 26
26constexpr std::size_t MainMemorySize = Common::Size_4_GB; 27constexpr std::size_t MainMemorySize = 4_GiB;
27constexpr std::size_t MainMemorySizeMax = Common::Size_8_GB; 28constexpr std::size_t MainMemorySizeMax = 8_GiB;
28 29
29constexpr std::size_t ReservedEarlyDramSize = 0x60000; 30constexpr std::size_t ReservedEarlyDramSize = 384_KiB;
30constexpr std::size_t DramPhysicalAddress = 0x80000000; 31constexpr std::size_t DramPhysicalAddress = 0x80000000;
31 32
32constexpr std::size_t KernelAslrAlignment = Common::Size_2_MB; 33constexpr std::size_t KernelAslrAlignment = 2_MiB;
33constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39; 34constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
34constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48; 35constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48;
35 36
@@ -40,7 +41,7 @@ constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceE
40constexpr std::size_t KernelVirtualAddressSpaceSize = 41constexpr std::size_t KernelVirtualAddressSpaceSize =
41 KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase; 42 KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase;
42constexpr std::size_t KernelVirtualAddressCodeBase = KernelVirtualAddressSpaceBase; 43constexpr std::size_t KernelVirtualAddressCodeBase = KernelVirtualAddressSpaceBase;
43constexpr std::size_t KernelVirtualAddressCodeSize = 0x62000; 44constexpr std::size_t KernelVirtualAddressCodeSize = 392_KiB;
44constexpr std::size_t KernelVirtualAddressCodeEnd = 45constexpr std::size_t KernelVirtualAddressCodeEnd =
45 KernelVirtualAddressCodeBase + KernelVirtualAddressCodeSize; 46 KernelVirtualAddressCodeBase + KernelVirtualAddressCodeSize;
46 47
@@ -53,14 +54,14 @@ constexpr std::size_t KernelPhysicalAddressSpaceSize =
53constexpr std::size_t KernelPhysicalAddressCodeBase = DramPhysicalAddress + ReservedEarlyDramSize; 54constexpr std::size_t KernelPhysicalAddressCodeBase = DramPhysicalAddress + ReservedEarlyDramSize;
54 55
55constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemorySizeMax); 56constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemorySizeMax);
56constexpr std::size_t KernelInitialPageHeapSize = Common::Size_128_KB; 57constexpr std::size_t KernelInitialPageHeapSize = 128_KiB;
57 58
58constexpr std::size_t KernelSlabHeapDataSize = Common::Size_5_MB; 59constexpr std::size_t KernelSlabHeapDataSize = 5_MiB;
59constexpr std::size_t KernelSlabHeapGapsSize = Common::Size_2_MB - Common::Size_64_KB; 60constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB;
60constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; 61constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize;
61 62
62// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. 63// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
63constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000ULL; 64constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB;
64 65
65constexpr std::size_t KernelResourceSize = 66constexpr std::size_t KernelResourceSize =
66 KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; 67 KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 66d260635..701268545 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/alignment.h" 5#include "common/alignment.h"
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/literals.h"
7#include "common/scope_exit.h" 8#include "common/scope_exit.h"
8#include "core/core.h" 9#include "core/core.h"
9#include "core/hle/kernel/k_address_space_info.h" 10#include "core/hle/kernel/k_address_space_info.h"
@@ -23,6 +24,8 @@ namespace Kernel {
23 24
24namespace { 25namespace {
25 26
27using namespace Common::Literals;
28
26constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) { 29constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) {
27 switch (as_type) { 30 switch (as_type) {
28 case FileSys::ProgramAddressSpaceType::Is32Bit: 31 case FileSys::ProgramAddressSpaceType::Is32Bit:
@@ -89,7 +92,7 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
89 } 92 }
90 93
91 // Set code regions and determine remaining 94 // Set code regions and determine remaining
92 constexpr std::size_t RegionAlignment{2 * 1024 * 1024}; 95 constexpr std::size_t RegionAlignment{2_MiB};
93 VAddr process_code_start{}; 96 VAddr process_code_start{};
94 VAddr process_code_end{}; 97 VAddr process_code_end{};
95 std::size_t stack_region_size{}; 98 std::size_t stack_region_size{};
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp
index da88f35bc..0c4bba66b 100644
--- a/src/core/hle/kernel/k_resource_limit.cpp
+++ b/src/core/hle/kernel/k_resource_limit.cpp
@@ -79,6 +79,7 @@ ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
79 R_UNLESS(current_values[index] <= value, ResultInvalidState); 79 R_UNLESS(current_values[index] <= value, ResultInvalidState);
80 80
81 limit_values[index] = value; 81 limit_values[index] = value;
82 peak_values[index] = current_values[index];
82 83
83 return ResultSuccess; 84 return ResultSuccess;
84} 85}
diff --git a/src/core/hle/kernel/k_trace.h b/src/core/hle/kernel/k_trace.h
index 91ebf9ab2..79391bccb 100644
--- a/src/core/hle/kernel/k_trace.h
+++ b/src/core/hle/kernel/k_trace.h
@@ -4,9 +4,13 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_funcs.h"
8
7namespace Kernel { 9namespace Kernel {
8 10
11using namespace Common::Literals;
12
9constexpr bool IsKTraceEnabled = false; 13constexpr bool IsKTraceEnabled = false;
10constexpr std::size_t KTraceBufferSize = IsKTraceEnabled ? 16 * 1024 * 1024 : 0; 14constexpr std::size_t KTraceBufferSize = IsKTraceEnabled ? 16_MiB : 0;
11 15
12} // namespace Kernel 16} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 2ceeaeb5f..64bd0c494 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -12,7 +12,6 @@
12#include <utility> 12#include <utility>
13 13
14#include "common/assert.h" 14#include "common/assert.h"
15#include "common/common_sizes.h"
16#include "common/logging/log.h" 15#include "common/logging/log.h"
17#include "common/microprofile.h" 16#include "common/microprofile.h"
18#include "common/thread.h" 17#include "common/thread.h"
@@ -180,7 +179,7 @@ struct KernelCore::Impl {
180 system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size); 179 system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size);
181 180
182 // Reserve secure applet memory, introduced in firmware 5.0.0 181 // Reserve secure applet memory, introduced in firmware 5.0.0
183 constexpr u64 secure_applet_memory_size{Common::Size_4_MB}; 182 constexpr u64 secure_applet_memory_size{4_MiB};
184 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 183 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
185 secure_applet_memory_size)); 184 secure_applet_memory_size));
186 185
@@ -320,8 +319,8 @@ struct KernelCore::Impl {
320 const VAddr code_end_virt_addr = KernelVirtualAddressCodeEnd; 319 const VAddr code_end_virt_addr = KernelVirtualAddressCodeEnd;
321 320
322 // Setup the containing kernel region. 321 // Setup the containing kernel region.
323 constexpr size_t KernelRegionSize = Common::Size_1_GB; 322 constexpr size_t KernelRegionSize = 1_GiB;
324 constexpr size_t KernelRegionAlign = Common::Size_1_GB; 323 constexpr size_t KernelRegionAlign = 1_GiB;
325 constexpr VAddr kernel_region_start = 324 constexpr VAddr kernel_region_start =
326 Common::AlignDown(code_start_virt_addr, KernelRegionAlign); 325 Common::AlignDown(code_start_virt_addr, KernelRegionAlign);
327 size_t kernel_region_size = KernelRegionSize; 326 size_t kernel_region_size = KernelRegionSize;
@@ -368,7 +367,7 @@ struct KernelCore::Impl {
368 367
369 // Decide on the actual size for the misc region. 368 // Decide on the actual size for the misc region.
370 constexpr size_t MiscRegionAlign = KernelAslrAlignment; 369 constexpr size_t MiscRegionAlign = KernelAslrAlignment;
371 constexpr size_t MiscRegionMinimumSize = Common::Size_32_MB; 370 constexpr size_t MiscRegionMinimumSize = 32_MiB;
372 const size_t misc_region_size = Common::AlignUp( 371 const size_t misc_region_size = Common::AlignUp(
373 std::max(misc_region_needed_size, MiscRegionMinimumSize), MiscRegionAlign); 372 std::max(misc_region_needed_size, MiscRegionMinimumSize), MiscRegionAlign);
374 ASSERT(misc_region_size > 0); 373 ASSERT(misc_region_size > 0);
@@ -381,7 +380,7 @@ struct KernelCore::Impl {
381 misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc)); 380 misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc));
382 381
383 // Setup the stack region. 382 // Setup the stack region.
384 constexpr size_t StackRegionSize = Common::Size_14_MB; 383 constexpr size_t StackRegionSize = 14_MiB;
385 constexpr size_t StackRegionAlign = KernelAslrAlignment; 384 constexpr size_t StackRegionAlign = KernelAslrAlignment;
386 const VAddr stack_region_start = 385 const VAddr stack_region_start =
387 memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( 386 memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
@@ -414,7 +413,7 @@ struct KernelCore::Impl {
414 slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab)); 413 slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab));
415 414
416 // Setup the temp region. 415 // Setup the temp region.
417 constexpr size_t TempRegionSize = Common::Size_128_MB; 416 constexpr size_t TempRegionSize = 128_MiB;
418 constexpr size_t TempRegionAlign = KernelAslrAlignment; 417 constexpr size_t TempRegionAlign = KernelAslrAlignment;
419 const VAddr temp_region_start = 418 const VAddr temp_region_start =
420 memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( 419 memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
@@ -470,7 +469,7 @@ struct KernelCore::Impl {
470 // Determine size available for kernel page table heaps, requiring > 8 MB. 469 // Determine size available for kernel page table heaps, requiring > 8 MB.
471 const PAddr resource_end_phys_addr = slab_start_phys_addr + resource_region_size; 470 const PAddr resource_end_phys_addr = slab_start_phys_addr + resource_region_size;
472 const size_t page_table_heap_size = resource_end_phys_addr - slab_end_phys_addr; 471 const size_t page_table_heap_size = resource_end_phys_addr - slab_end_phys_addr;
473 ASSERT(page_table_heap_size / Common::Size_4_MB > 2); 472 ASSERT(page_table_heap_size / 4_MiB > 2);
474 473
475 // Insert a physical region for the kernel page table heap region 474 // Insert a physical region for the kernel page table heap region
476 ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( 475 ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
@@ -495,7 +494,7 @@ struct KernelCore::Impl {
495 ASSERT(linear_extents.GetEndAddress() != 0); 494 ASSERT(linear_extents.GetEndAddress() != 0);
496 495
497 // Setup the linear mapping region. 496 // Setup the linear mapping region.
498 constexpr size_t LinearRegionAlign = Common::Size_1_GB; 497 constexpr size_t LinearRegionAlign = 1_GiB;
499 const PAddr aligned_linear_phys_start = 498 const PAddr aligned_linear_phys_start =
500 Common::AlignDown(linear_extents.GetAddress(), LinearRegionAlign); 499 Common::AlignDown(linear_extents.GetAddress(), LinearRegionAlign);
501 const size_t linear_region_size = 500 const size_t linear_region_size =
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index fec704c65..dd945e058 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -117,7 +117,7 @@ AOC_U::AOC_U(Core::System& system_)
117 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, 117 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
118 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, 118 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
119 {9, nullptr, "GetAddOnContentLostErrorCode"}, 119 {9, nullptr, "GetAddOnContentLostErrorCode"},
120 {10, nullptr, "GetAddOnContentListChangedEventWithProcessId"}, 120 {10, &AOC_U::GetAddOnContentListChangedEventWithProcessId, "GetAddOnContentListChangedEventWithProcessId"},
121 {100, &AOC_U::CreateEcPurchasedEventManager, "CreateEcPurchasedEventManager"}, 121 {100, &AOC_U::CreateEcPurchasedEventManager, "CreateEcPurchasedEventManager"},
122 {101, &AOC_U::CreatePermanentEcPurchasedEventManager, "CreatePermanentEcPurchasedEventManager"}, 122 {101, &AOC_U::CreatePermanentEcPurchasedEventManager, "CreatePermanentEcPurchasedEventManager"},
123 {110, nullptr, "CreateContentsServiceManager"}, 123 {110, nullptr, "CreateContentsServiceManager"},
@@ -257,6 +257,14 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
257 rb.PushCopyObjects(aoc_change_event.GetReadableEvent()); 257 rb.PushCopyObjects(aoc_change_event.GetReadableEvent());
258} 258}
259 259
260void AOC_U::GetAddOnContentListChangedEventWithProcessId(Kernel::HLERequestContext& ctx) {
261 LOG_WARNING(Service_AOC, "(STUBBED) called");
262
263 IPC::ResponseBuilder rb{ctx, 2, 1};
264 rb.Push(ResultSuccess);
265 rb.PushCopyObjects(aoc_change_event.GetReadableEvent());
266}
267
260void AOC_U::CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx) { 268void AOC_U::CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx) {
261 LOG_WARNING(Service_AOC, "(STUBBED) called"); 269 LOG_WARNING(Service_AOC, "(STUBBED) called");
262 270
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index 65095baa2..bb6ffb8eb 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -28,6 +28,7 @@ private:
28 void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx); 28 void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx);
29 void PrepareAddOnContent(Kernel::HLERequestContext& ctx); 29 void PrepareAddOnContent(Kernel::HLERequestContext& ctx);
30 void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx); 30 void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx);
31 void GetAddOnContentListChangedEventWithProcessId(Kernel::HLERequestContext& ctx);
31 void CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx); 32 void CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx);
32 void CreatePermanentEcPurchasedEventManager(Kernel::HLERequestContext& ctx); 33 void CreatePermanentEcPurchasedEventManager(Kernel::HLERequestContext& ctx);
33 34
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 804c6b10c..92d4510b1 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -58,7 +58,7 @@ public:
58 {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, 58 {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
59 {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, 59 {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
60 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, 60 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
61 {10, nullptr, "GetAudioOutPlayedSampleCount"}, 61 {10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"},
62 {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"}, 62 {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
63 {12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"}, 63 {12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
64 {13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"}, 64 {13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
@@ -186,6 +186,14 @@ private:
186 rb.Push(static_cast<u32>(stream->GetQueueSize())); 186 rb.Push(static_cast<u32>(stream->GetQueueSize()));
187 } 187 }
188 188
189 void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) {
190 LOG_DEBUG(Service_Audio, "called");
191
192 IPC::ResponseBuilder rb{ctx, 4};
193 rb.Push(ResultSuccess);
194 rb.Push(stream->GetPlayedSampleCount());
195 }
196
189 void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) { 197 void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
190 LOG_DEBUG(Service_Audio, "called"); 198 LOG_DEBUG(Service_Audio, "called");
191 199
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 10e6f7a64..33a6dbbb6 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -253,7 +253,11 @@ void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
253 rb.Push<u32>(worker_buffer_sz); 253 rb.Push<u32>(worker_buffer_sz);
254} 254}
255 255
256void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { 256void HwOpus::GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx) {
257 GetWorkBufferSize(ctx);
258}
259
260void HwOpus::OpenHardwareOpusDecoder(Kernel::HLERequestContext& ctx) {
257 IPC::RequestParser rp{ctx}; 261 IPC::RequestParser rp{ctx};
258 const auto sample_rate = rp.Pop<u32>(); 262 const auto sample_rate = rp.Pop<u32>();
259 const auto channel_count = rp.Pop<u32>(); 263 const auto channel_count = rp.Pop<u32>();
@@ -291,14 +295,47 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
291 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); 295 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
292} 296}
293 297
298void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) {
299 IPC::RequestParser rp{ctx};
300 const auto sample_rate = rp.Pop<u32>();
301 const auto channel_count = rp.Pop<u32>();
302
303 LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
304
305 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
306 sample_rate == 12000 || sample_rate == 8000,
307 "Invalid sample rate");
308 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
309
310 const int num_stereo_streams = channel_count == 2 ? 1 : 0;
311 const auto mapping_table = CreateMappingTable(channel_count);
312
313 int error = 0;
314 OpusDecoderPtr decoder{
315 opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1,
316 num_stereo_streams, mapping_table.data(), &error)};
317 if (error != OPUS_OK || decoder == nullptr) {
318 LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
319 IPC::ResponseBuilder rb{ctx, 2};
320 // TODO(ogniK): Use correct error code
321 rb.Push(ResultUnknown);
322 return;
323 }
324
325 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
326 rb.Push(ResultSuccess);
327 rb.PushIpcInterface<IHardwareOpusDecoderManager>(
328 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
329}
330
294HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { 331HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
295 static const FunctionInfo functions[] = { 332 static const FunctionInfo functions[] = {
296 {0, &HwOpus::OpenOpusDecoder, "OpenOpusDecoder"}, 333 {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"},
297 {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, 334 {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"},
298 {2, nullptr, "OpenOpusDecoderForMultiStream"}, 335 {2, nullptr, "OpenOpusDecoderForMultiStream"},
299 {3, nullptr, "GetWorkBufferSizeForMultiStream"}, 336 {3, nullptr, "GetWorkBufferSizeForMultiStream"},
300 {4, nullptr, "OpenHardwareOpusDecoderEx"}, 337 {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},
301 {5, nullptr, "GetWorkBufferSizeEx"}, 338 {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},
302 {6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"}, 339 {6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"},
303 {7, nullptr, "GetWorkBufferSizeForMultiStreamEx"}, 340 {7, nullptr, "GetWorkBufferSizeForMultiStreamEx"},
304 }; 341 };
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h
index 4f921f18e..b74824ff3 100644
--- a/src/core/hle/service/audio/hwopus.h
+++ b/src/core/hle/service/audio/hwopus.h
@@ -18,8 +18,10 @@ public:
18 ~HwOpus() override; 18 ~HwOpus() override;
19 19
20private: 20private:
21 void OpenOpusDecoder(Kernel::HLERequestContext& ctx); 21 void OpenHardwareOpusDecoder(Kernel::HLERequestContext& ctx);
22 void OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx);
22 void GetWorkBufferSize(Kernel::HLERequestContext& ctx); 23 void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
24 void GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx);
23}; 25};
24 26
25} // namespace Service::Audio 27} // namespace Service::Audio
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index a2844ea8c..dc15cf58b 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -313,7 +313,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
313 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 313 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
314 314
315 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 315 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
316 void(Common::FS::RemoveFile(zip_path)); 316 Common::FS::RemoveFile(zip_path);
317 } 317 }
318 318
319 HandleDownloadDisplayResult(applet_manager, res); 319 HandleDownloadDisplayResult(applet_manager, res);
@@ -445,7 +445,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
445 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 445 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
446 446
447 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 447 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
448 void(Common::FS::RemoveFile(bin_file_path)); 448 Common::FS::RemoveFile(bin_file_path);
449 } 449 }
450 450
451 HandleDownloadDisplayResult(applet_manager, res); 451 HandleDownloadDisplayResult(applet_manager, res);
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 7acad3798..1eb02aee2 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -314,6 +314,8 @@ void Controller_NPad::OnInit() {
314 314
315void Controller_NPad::OnLoadInputDevices() { 315void Controller_NPad::OnLoadInputDevices() {
316 const auto& players = Settings::values.players.GetValue(); 316 const auto& players = Settings::values.players.GetValue();
317
318 std::lock_guard lock{mutex};
317 for (std::size_t i = 0; i < players.size(); ++i) { 319 for (std::size_t i = 0; i < players.size(); ++i) {
318 std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 320 std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
319 players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 321 players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
@@ -348,6 +350,8 @@ void Controller_NPad::OnRelease() {
348} 350}
349 351
350void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { 352void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
353 std::lock_guard lock{mutex};
354
351 const auto controller_idx = NPadIdToIndex(npad_id); 355 const auto controller_idx = NPadIdToIndex(npad_id);
352 const auto controller_type = connected_controllers[controller_idx].type; 356 const auto controller_type = connected_controllers[controller_idx].type;
353 if (!connected_controllers[controller_idx].is_connected) { 357 if (!connected_controllers[controller_idx].is_connected) {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index c050c9a44..1409d82a2 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -6,6 +6,8 @@
6 6
7#include <array> 7#include <array>
8#include <atomic> 8#include <atomic>
9#include <mutex>
10
9#include "common/bit_field.h" 11#include "common/bit_field.h"
10#include "common/common_types.h" 12#include "common/common_types.h"
11#include "common/quaternion.h" 13#include "common/quaternion.h"
@@ -563,6 +565,8 @@ private:
563 using MotionArray = std::array< 565 using MotionArray = std::array<
564 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>, 566 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
565 10>; 567 10>;
568
569 std::mutex mutex;
566 ButtonArray buttons; 570 ButtonArray buttons;
567 StickArray sticks; 571 StickArray sticks;
568 VibrationArray vibrations; 572 VibrationArray vibrations;
diff --git a/src/core/hle/service/mii/manager.h b/src/core/hle/service/mii/manager.h
index ec7efa5f7..8e048fc56 100644
--- a/src/core/hle/service/mii/manager.h
+++ b/src/core/hle/service/mii/manager.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <vector>
7#include "common/bit_field.h" 9#include "common/bit_field.h"
8#include "common/common_funcs.h" 10#include "common/common_funcs.h"
9#include "common/uuid.h" 11#include "common/uuid.h"
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index d1dbc659b..1d810562f 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -307,6 +307,9 @@ void NVFlinger::Compose() {
307} 307}
308 308
309s64 NVFlinger::GetNextTicks() const { 309s64 NVFlinger::GetNextTicks() const {
310 if (Settings::values.disable_fps_limit.GetValue()) {
311 return 0;
312 }
310 constexpr s64 max_hertz = 120LL; 313 constexpr s64 max_hertz = 120LL;
311 return (1000000000 * (1LL << swap_interval)) / max_hertz; 314 return (1000000000 * (1LL << swap_interval)) / max_hertz;
312} 315}
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 4e1541630..663b83cd3 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -149,10 +149,10 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
149 std::string function_name = info == nullptr ? fmt::format("{}", ctx.GetCommand()) : info->name; 149 std::string function_name = info == nullptr ? fmt::format("{}", ctx.GetCommand()) : info->name;
150 150
151 fmt::memory_buffer buf; 151 fmt::memory_buffer buf;
152 fmt::format_to(buf, "function '{}': port='{}' cmd_buf={{[0]=0x{:X}", function_name, 152 fmt::format_to(std::back_inserter(buf), "function '{}': port='{}' cmd_buf={{[0]=0x{:X}",
153 service_name, cmd_buf[0]); 153 function_name, service_name, cmd_buf[0]);
154 for (int i = 1; i <= 8; ++i) { 154 for (int i = 1; i <= 8; ++i) {
155 fmt::format_to(buf, ", [{}]=0x{:X}", i, cmd_buf[i]); 155 fmt::format_to(std::back_inserter(buf), ", [{}]=0x{:X}", i, cmd_buf[i]);
156 } 156 }
157 buf.push_back('}'); 157 buf.push_back('}');
158 158
diff --git a/src/core/hle/service/spl/csrng.cpp b/src/core/hle/service/spl/csrng.cpp
index 1beca417c..9c7f89475 100644
--- a/src/core/hle/service/spl/csrng.cpp
+++ b/src/core/hle/service/spl/csrng.cpp
@@ -9,7 +9,7 @@ namespace Service::SPL {
9CSRNG::CSRNG(Core::System& system_, std::shared_ptr<Module> module_) 9CSRNG::CSRNG(Core::System& system_, std::shared_ptr<Module> module_)
10 : Interface(system_, std::move(module_), "csrng") { 10 : Interface(system_, std::move(module_), "csrng") {
11 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
12 {0, &CSRNG::GetRandomBytes, "GetRandomBytes"}, 12 {0, &CSRNG::GenerateRandomBytes, "GenerateRandomBytes"},
13 }; 13 };
14 RegisterHandlers(functions); 14 RegisterHandlers(functions);
15} 15}
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index 0b5e2b7c3..ebb179aa8 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -10,6 +10,7 @@
10#include <vector> 10#include <vector>
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/settings.h" 12#include "common/settings.h"
13#include "core/hle/api_version.h"
13#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
14#include "core/hle/service/spl/csrng.h" 15#include "core/hle/service/spl/csrng.h"
15#include "core/hle/service/spl/module.h" 16#include "core/hle/service/spl/module.h"
@@ -24,7 +25,46 @@ Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> modu
24 25
25Module::Interface::~Interface() = default; 26Module::Interface::~Interface() = default;
26 27
27void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { 28void Module::Interface::GetConfig(Kernel::HLERequestContext& ctx) {
29 IPC::RequestParser rp{ctx};
30 const auto config_item = rp.PopEnum<ConfigItem>();
31
32 // This should call svcCallSecureMonitor with the appropriate args.
33 // Since we do not have it implemented yet, we will use this for now.
34 const auto smc_result = GetConfigImpl(config_item);
35 const auto result_code = smc_result.Code();
36
37 if (smc_result.Failed()) {
38 LOG_ERROR(Service_SPL, "called, config_item={}, result_code={}", config_item,
39 result_code.raw);
40
41 IPC::ResponseBuilder rb{ctx, 2};
42 rb.Push(result_code);
43 }
44
45 LOG_DEBUG(Service_SPL, "called, config_item={}, result_code={}, smc_result={}", config_item,
46 result_code.raw, *smc_result);
47
48 IPC::ResponseBuilder rb{ctx, 4};
49 rb.Push(result_code);
50 rb.Push(*smc_result);
51}
52
53void Module::Interface::ModularExponentiate(Kernel::HLERequestContext& ctx) {
54 UNIMPLEMENTED_MSG("ModularExponentiate is not implemented!");
55
56 IPC::ResponseBuilder rb{ctx, 2};
57 rb.Push(ResultSecureMonitorNotImplemented);
58}
59
60void Module::Interface::SetConfig(Kernel::HLERequestContext& ctx) {
61 UNIMPLEMENTED_MSG("SetConfig is not implemented!");
62
63 IPC::ResponseBuilder rb{ctx, 2};
64 rb.Push(ResultSecureMonitorNotImplemented);
65}
66
67void Module::Interface::GenerateRandomBytes(Kernel::HLERequestContext& ctx) {
28 LOG_DEBUG(Service_SPL, "called"); 68 LOG_DEBUG(Service_SPL, "called");
29 69
30 const std::size_t size = ctx.GetWriteBufferSize(); 70 const std::size_t size = ctx.GetWriteBufferSize();
@@ -39,6 +79,88 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
39 rb.Push(ResultSuccess); 79 rb.Push(ResultSuccess);
40} 80}
41 81
82void Module::Interface::IsDevelopment(Kernel::HLERequestContext& ctx) {
83 UNIMPLEMENTED_MSG("IsDevelopment is not implemented!");
84
85 IPC::ResponseBuilder rb{ctx, 2};
86 rb.Push(ResultSecureMonitorNotImplemented);
87}
88
89void Module::Interface::SetBootReason(Kernel::HLERequestContext& ctx) {
90 UNIMPLEMENTED_MSG("SetBootReason is not implemented!");
91
92 IPC::ResponseBuilder rb{ctx, 2};
93 rb.Push(ResultSecureMonitorNotImplemented);
94}
95
96void Module::Interface::GetBootReason(Kernel::HLERequestContext& ctx) {
97 UNIMPLEMENTED_MSG("GetBootReason is not implemented!");
98
99 IPC::ResponseBuilder rb{ctx, 2};
100 rb.Push(ResultSecureMonitorNotImplemented);
101}
102
103ResultVal<u64> Module::Interface::GetConfigImpl(ConfigItem config_item) const {
104 switch (config_item) {
105 case ConfigItem::DisableProgramVerification:
106 case ConfigItem::DramId:
107 case ConfigItem::SecurityEngineInterruptNumber:
108 case ConfigItem::FuseVersion:
109 case ConfigItem::HardwareType:
110 case ConfigItem::HardwareState:
111 case ConfigItem::IsRecoveryBoot:
112 case ConfigItem::DeviceId:
113 case ConfigItem::BootReason:
114 case ConfigItem::MemoryMode:
115 case ConfigItem::IsDevelopmentFunctionEnabled:
116 case ConfigItem::KernelConfiguration:
117 case ConfigItem::IsChargerHiZModeEnabled:
118 case ConfigItem::QuestState:
119 case ConfigItem::RegulatorType:
120 case ConfigItem::DeviceUniqueKeyGeneration:
121 case ConfigItem::Package2Hash:
122 return ResultSecureMonitorNotImplemented;
123 case ConfigItem::ExosphereApiVersion:
124 // Get information about the current exosphere version.
125 return MakeResult((u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MAJOR} << 56) |
126 (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MINOR} << 48) |
127 (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MICRO} << 40) |
128 (static_cast<u64>(HLE::ApiVersion::GetTargetFirmware())));
129 case ConfigItem::ExosphereNeedsReboot:
130 // We are executing, so we aren't in the process of rebooting.
131 return MakeResult(u64{0});
132 case ConfigItem::ExosphereNeedsShutdown:
133 // We are executing, so we aren't in the process of shutting down.
134 return MakeResult(u64{0});
135 case ConfigItem::ExosphereGitCommitHash:
136 // Get information about the current exosphere git commit hash.
137 return MakeResult(u64{0});
138 case ConfigItem::ExosphereHasRcmBugPatch:
139 // Get information about whether this unit has the RCM bug patched.
140 return MakeResult(u64{0});
141 case ConfigItem::ExosphereBlankProdInfo:
142 // Get whether this unit should simulate a "blanked" PRODINFO.
143 return MakeResult(u64{0});
144 case ConfigItem::ExosphereAllowCalWrites:
145 // Get whether this unit should allow writing to the calibration partition.
146 return MakeResult(u64{0});
147 case ConfigItem::ExosphereEmummcType:
148 // Get what kind of emummc this unit has active.
149 return MakeResult(u64{0});
150 case ConfigItem::ExospherePayloadAddress:
151 // Gets the physical address of the reboot payload buffer, if one exists.
152 return ResultSecureMonitorNotInitialized;
153 case ConfigItem::ExosphereLogConfiguration:
154 // Get the log configuration.
155 return MakeResult(u64{0});
156 case ConfigItem::ExosphereForceEnableUsb30:
157 // Get whether usb 3.0 should be force-enabled.
158 return MakeResult(u64{0});
159 default:
160 return ResultSecureMonitorInvalidArgument;
161 }
162}
163
42void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 164void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
43 auto module = std::make_shared<Module>(); 165 auto module = std::make_shared<Module>();
44 std::make_shared<CSRNG>(system, module)->InstallAsService(service_manager); 166 std::make_shared<CSRNG>(system, module)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h
index 71855c1bf..61630df80 100644
--- a/src/core/hle/service/spl/module.h
+++ b/src/core/hle/service/spl/module.h
@@ -6,6 +6,8 @@
6 6
7#include <random> 7#include <random>
8#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
9#include "core/hle/service/spl/spl_results.h"
10#include "core/hle/service/spl/spl_types.h"
9 11
10namespace Core { 12namespace Core {
11class System; 13class System;
@@ -21,12 +23,21 @@ public:
21 const char* name); 23 const char* name);
22 ~Interface() override; 24 ~Interface() override;
23 25
24 void GetRandomBytes(Kernel::HLERequestContext& ctx); 26 // General
27 void GetConfig(Kernel::HLERequestContext& ctx);
28 void ModularExponentiate(Kernel::HLERequestContext& ctx);
29 void SetConfig(Kernel::HLERequestContext& ctx);
30 void GenerateRandomBytes(Kernel::HLERequestContext& ctx);
31 void IsDevelopment(Kernel::HLERequestContext& ctx);
32 void SetBootReason(Kernel::HLERequestContext& ctx);
33 void GetBootReason(Kernel::HLERequestContext& ctx);
25 34
26 protected: 35 protected:
27 std::shared_ptr<Module> module; 36 std::shared_ptr<Module> module;
28 37
29 private: 38 private:
39 ResultVal<u64> GetConfigImpl(ConfigItem config_item) const;
40
30 std::mt19937 rng; 41 std::mt19937 rng;
31 }; 42 };
32}; 43};
diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp
index fff3f3c42..20384042f 100644
--- a/src/core/hle/service/spl/spl.cpp
+++ b/src/core/hle/service/spl/spl.cpp
@@ -10,13 +10,13 @@ SPL::SPL(Core::System& system_, std::shared_ptr<Module> module_)
10 : Interface(system_, std::move(module_), "spl:") { 10 : Interface(system_, std::move(module_), "spl:") {
11 // clang-format off 11 // clang-format off
12 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
13 {0, nullptr, "GetConfig"}, 13 {0, &SPL::GetConfig, "GetConfig"},
14 {1, nullptr, "ModularExponentiate"}, 14 {1, &SPL::ModularExponentiate, "ModularExponentiate"},
15 {5, nullptr, "SetConfig"}, 15 {5, &SPL::SetConfig, "SetConfig"},
16 {7, &SPL::GetRandomBytes, "GetRandomBytes"}, 16 {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
17 {11, nullptr, "IsDevelopment"}, 17 {11, &SPL::IsDevelopment, "IsDevelopment"},
18 {24, nullptr, "SetBootReason"}, 18 {24, &SPL::SetBootReason, "SetBootReason"},
19 {25, nullptr, "GetBootReason"}, 19 {25, &SPL::GetBootReason, "GetBootReason"},
20 }; 20 };
21 // clang-format on 21 // clang-format on
22 22
@@ -27,22 +27,22 @@ SPL_MIG::SPL_MIG(Core::System& system_, std::shared_ptr<Module> module_)
27 : Interface(system_, std::move(module_), "spl:mig") { 27 : Interface(system_, std::move(module_), "spl:mig") {
28 // clang-format off 28 // clang-format off
29 static const FunctionInfo functions[] = { 29 static const FunctionInfo functions[] = {
30 {0, nullptr, "GetConfig"}, 30 {0, &SPL::GetConfig, "GetConfig"},
31 {1, nullptr, "ModularExponentiate"}, 31 {1, &SPL::ModularExponentiate, "ModularExponentiate"},
32 {2, nullptr, "GenerateAesKek"}, 32 {2, nullptr, "GenerateAesKek"},
33 {3, nullptr, "LoadAesKey"}, 33 {3, nullptr, "LoadAesKey"},
34 {4, nullptr, "GenerateAesKey"}, 34 {4, nullptr, "GenerateAesKey"},
35 {5, nullptr, "SetConfig"}, 35 {5, &SPL::SetConfig, "SetConfig"},
36 {7, &SPL::GetRandomBytes, "GenerateRandomBytes"}, 36 {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
37 {11, nullptr, "IsDevelopment"}, 37 {11, &SPL::IsDevelopment, "IsDevelopment"},
38 {14, nullptr, "DecryptAesKey"}, 38 {14, nullptr, "DecryptAesKey"},
39 {15, nullptr, "CryptAesCtr"}, 39 {15, nullptr, "CryptAesCtr"},
40 {16, nullptr, "ComputeCmac"}, 40 {16, nullptr, "ComputeCmac"},
41 {21, nullptr, "AllocateAesKeyslot"}, 41 {21, nullptr, "AllocateAesKeyslot"},
42 {22, nullptr, "DeallocateAesKeySlot"}, 42 {22, nullptr, "DeallocateAesKeySlot"},
43 {23, nullptr, "GetAesKeyslotAvailableEvent"}, 43 {23, nullptr, "GetAesKeyslotAvailableEvent"},
44 {24, nullptr, "SetBootReason"}, 44 {24, &SPL::SetBootReason, "SetBootReason"},
45 {25, nullptr, "GetBootReason"}, 45 {25, &SPL::GetBootReason, "GetBootReason"},
46 }; 46 };
47 // clang-format on 47 // clang-format on
48 48
@@ -53,16 +53,16 @@ SPL_FS::SPL_FS(Core::System& system_, std::shared_ptr<Module> module_)
53 : Interface(system_, std::move(module_), "spl:fs") { 53 : Interface(system_, std::move(module_), "spl:fs") {
54 // clang-format off 54 // clang-format off
55 static const FunctionInfo functions[] = { 55 static const FunctionInfo functions[] = {
56 {0, nullptr, "GetConfig"}, 56 {0, &SPL::GetConfig, "GetConfig"},
57 {1, nullptr, "ModularExponentiate"}, 57 {1, &SPL::ModularExponentiate, "ModularExponentiate"},
58 {2, nullptr, "GenerateAesKek"}, 58 {2, nullptr, "GenerateAesKek"},
59 {3, nullptr, "LoadAesKey"}, 59 {3, nullptr, "LoadAesKey"},
60 {4, nullptr, "GenerateAesKey"}, 60 {4, nullptr, "GenerateAesKey"},
61 {5, nullptr, "SetConfig"}, 61 {5, &SPL::SetConfig, "SetConfig"},
62 {7, &SPL::GetRandomBytes, "GenerateRandomBytes"}, 62 {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
63 {9, nullptr, "ImportLotusKey"}, 63 {9, nullptr, "ImportLotusKey"},
64 {10, nullptr, "DecryptLotusMessage"}, 64 {10, nullptr, "DecryptLotusMessage"},
65 {11, nullptr, "IsDevelopment"}, 65 {11, &SPL::IsDevelopment, "IsDevelopment"},
66 {12, nullptr, "GenerateSpecificAesKey"}, 66 {12, nullptr, "GenerateSpecificAesKey"},
67 {14, nullptr, "DecryptAesKey"}, 67 {14, nullptr, "DecryptAesKey"},
68 {15, nullptr, "CryptAesCtr"}, 68 {15, nullptr, "CryptAesCtr"},
@@ -71,8 +71,8 @@ SPL_FS::SPL_FS(Core::System& system_, std::shared_ptr<Module> module_)
71 {21, nullptr, "AllocateAesKeyslot"}, 71 {21, nullptr, "AllocateAesKeyslot"},
72 {22, nullptr, "DeallocateAesKeySlot"}, 72 {22, nullptr, "DeallocateAesKeySlot"},
73 {23, nullptr, "GetAesKeyslotAvailableEvent"}, 73 {23, nullptr, "GetAesKeyslotAvailableEvent"},
74 {24, nullptr, "SetBootReason"}, 74 {24, &SPL::SetBootReason, "SetBootReason"},
75 {25, nullptr, "GetBootReason"}, 75 {25, &SPL::GetBootReason, "GetBootReason"},
76 {31, nullptr, "GetPackage2Hash"}, 76 {31, nullptr, "GetPackage2Hash"},
77 }; 77 };
78 // clang-format on 78 // clang-format on
@@ -84,14 +84,14 @@ SPL_SSL::SPL_SSL(Core::System& system_, std::shared_ptr<Module> module_)
84 : Interface(system_, std::move(module_), "spl:ssl") { 84 : Interface(system_, std::move(module_), "spl:ssl") {
85 // clang-format off 85 // clang-format off
86 static const FunctionInfo functions[] = { 86 static const FunctionInfo functions[] = {
87 {0, nullptr, "GetConfig"}, 87 {0, &SPL::GetConfig, "GetConfig"},
88 {1, nullptr, "ModularExponentiate"}, 88 {1, &SPL::ModularExponentiate, "ModularExponentiate"},
89 {2, nullptr, "GenerateAesKek"}, 89 {2, nullptr, "GenerateAesKek"},
90 {3, nullptr, "LoadAesKey"}, 90 {3, nullptr, "LoadAesKey"},
91 {4, nullptr, "GenerateAesKey"}, 91 {4, nullptr, "GenerateAesKey"},
92 {5, nullptr, "SetConfig"}, 92 {5, &SPL::SetConfig, "SetConfig"},
93 {7, &SPL::GetRandomBytes, "GetRandomBytes"}, 93 {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
94 {11, nullptr, "IsDevelopment"}, 94 {11, &SPL::IsDevelopment, "IsDevelopment"},
95 {13, nullptr, "DecryptDeviceUniqueData"}, 95 {13, nullptr, "DecryptDeviceUniqueData"},
96 {14, nullptr, "DecryptAesKey"}, 96 {14, nullptr, "DecryptAesKey"},
97 {15, nullptr, "CryptAesCtr"}, 97 {15, nullptr, "CryptAesCtr"},
@@ -99,8 +99,8 @@ SPL_SSL::SPL_SSL(Core::System& system_, std::shared_ptr<Module> module_)
99 {21, nullptr, "AllocateAesKeyslot"}, 99 {21, nullptr, "AllocateAesKeyslot"},
100 {22, nullptr, "DeallocateAesKeySlot"}, 100 {22, nullptr, "DeallocateAesKeySlot"},
101 {23, nullptr, "GetAesKeyslotAvailableEvent"}, 101 {23, nullptr, "GetAesKeyslotAvailableEvent"},
102 {24, nullptr, "SetBootReason"}, 102 {24, &SPL::SetBootReason, "SetBootReason"},
103 {25, nullptr, "GetBootReason"}, 103 {25, &SPL::GetBootReason, "GetBootReason"},
104 {26, nullptr, "DecryptAndStoreSslClientCertKey"}, 104 {26, nullptr, "DecryptAndStoreSslClientCertKey"},
105 {27, nullptr, "ModularExponentiateWithSslClientCertKey"}, 105 {27, nullptr, "ModularExponentiateWithSslClientCertKey"},
106 }; 106 };
@@ -113,14 +113,14 @@ SPL_ES::SPL_ES(Core::System& system_, std::shared_ptr<Module> module_)
113 : Interface(system_, std::move(module_), "spl:es") { 113 : Interface(system_, std::move(module_), "spl:es") {
114 // clang-format off 114 // clang-format off
115 static const FunctionInfo functions[] = { 115 static const FunctionInfo functions[] = {
116 {0, nullptr, "GetConfig"}, 116 {0, &SPL::GetConfig, "GetConfig"},
117 {1, nullptr, "ModularExponentiate"}, 117 {1, &SPL::ModularExponentiate, "ModularExponentiate"},
118 {2, nullptr, "GenerateAesKek"}, 118 {2, nullptr, "GenerateAesKek"},
119 {3, nullptr, "LoadAesKey"}, 119 {3, nullptr, "LoadAesKey"},
120 {4, nullptr, "GenerateAesKey"}, 120 {4, nullptr, "GenerateAesKey"},
121 {5, nullptr, "SetConfig"}, 121 {5, &SPL::SetConfig, "SetConfig"},
122 {7, &SPL::GetRandomBytes, "GenerateRandomBytes"}, 122 {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
123 {11, nullptr, "IsDevelopment"}, 123 {11, &SPL::IsDevelopment, "IsDevelopment"},
124 {13, nullptr, "DecryptDeviceUniqueData"}, 124 {13, nullptr, "DecryptDeviceUniqueData"},
125 {14, nullptr, "DecryptAesKey"}, 125 {14, nullptr, "DecryptAesKey"},
126 {15, nullptr, "CryptAesCtr"}, 126 {15, nullptr, "CryptAesCtr"},
@@ -131,8 +131,8 @@ SPL_ES::SPL_ES(Core::System& system_, std::shared_ptr<Module> module_)
131 {21, nullptr, "AllocateAesKeyslot"}, 131 {21, nullptr, "AllocateAesKeyslot"},
132 {22, nullptr, "DeallocateAesKeySlot"}, 132 {22, nullptr, "DeallocateAesKeySlot"},
133 {23, nullptr, "GetAesKeyslotAvailableEvent"}, 133 {23, nullptr, "GetAesKeyslotAvailableEvent"},
134 {24, nullptr, "SetBootReason"}, 134 {24, &SPL::SetBootReason, "SetBootReason"},
135 {25, nullptr, "GetBootReason"}, 135 {25, &SPL::GetBootReason, "GetBootReason"},
136 {28, nullptr, "DecryptAndStoreDrmDeviceCertKey"}, 136 {28, nullptr, "DecryptAndStoreDrmDeviceCertKey"},
137 {29, nullptr, "ModularExponentiateWithDrmDeviceCertKey"}, 137 {29, nullptr, "ModularExponentiateWithDrmDeviceCertKey"},
138 {31, nullptr, "PrepareEsArchiveKey"}, 138 {31, nullptr, "PrepareEsArchiveKey"},
@@ -147,14 +147,14 @@ SPL_MANU::SPL_MANU(Core::System& system_, std::shared_ptr<Module> module_)
147 : Interface(system_, std::move(module_), "spl:manu") { 147 : Interface(system_, std::move(module_), "spl:manu") {
148 // clang-format off 148 // clang-format off
149 static const FunctionInfo functions[] = { 149 static const FunctionInfo functions[] = {
150 {0, nullptr, "GetConfig"}, 150 {0, &SPL::GetConfig, "GetConfig"},
151 {1, nullptr, "ModularExponentiate"}, 151 {1, &SPL::ModularExponentiate, "ModularExponentiate"},
152 {2, nullptr, "GenerateAesKek"}, 152 {2, nullptr, "GenerateAesKek"},
153 {3, nullptr, "LoadAesKey"}, 153 {3, nullptr, "LoadAesKey"},
154 {4, nullptr, "GenerateAesKey"}, 154 {4, nullptr, "GenerateAesKey"},
155 {5, nullptr, "SetConfig"}, 155 {5, &SPL::SetConfig, "SetConfig"},
156 {7, &SPL::GetRandomBytes, "GetRandomBytes"}, 156 {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
157 {11, nullptr, "IsDevelopment"}, 157 {11, &SPL::IsDevelopment, "IsDevelopment"},
158 {13, nullptr, "DecryptDeviceUniqueData"}, 158 {13, nullptr, "DecryptDeviceUniqueData"},
159 {14, nullptr, "DecryptAesKey"}, 159 {14, nullptr, "DecryptAesKey"},
160 {15, nullptr, "CryptAesCtr"}, 160 {15, nullptr, "CryptAesCtr"},
@@ -162,8 +162,8 @@ SPL_MANU::SPL_MANU(Core::System& system_, std::shared_ptr<Module> module_)
162 {21, nullptr, "AllocateAesKeyslot"}, 162 {21, nullptr, "AllocateAesKeyslot"},
163 {22, nullptr, "DeallocateAesKeySlot"}, 163 {22, nullptr, "DeallocateAesKeySlot"},
164 {23, nullptr, "GetAesKeyslotAvailableEvent"}, 164 {23, nullptr, "GetAesKeyslotAvailableEvent"},
165 {24, nullptr, "SetBootReason"}, 165 {24, &SPL::SetBootReason, "SetBootReason"},
166 {25, nullptr, "GetBootReason"}, 166 {25, &SPL::GetBootReason, "GetBootReason"},
167 {30, nullptr, "ReencryptDeviceUniqueData"}, 167 {30, nullptr, "ReencryptDeviceUniqueData"},
168 }; 168 };
169 // clang-format on 169 // clang-format on
diff --git a/src/core/hle/service/spl/spl_results.h b/src/core/hle/service/spl/spl_results.h
new file mode 100644
index 000000000..a07c61409
--- /dev/null
+++ b/src/core/hle/service/spl/spl_results.h
@@ -0,0 +1,31 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8
9namespace Service::SPL {
10
11// Description 0 - 99
12constexpr ResultCode ResultSecureMonitorError{ErrorModule::SPL, 0};
13constexpr ResultCode ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1};
14constexpr ResultCode ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2};
15constexpr ResultCode ResultSecureMonitorBusy{ErrorModule::SPL, 3};
16constexpr ResultCode ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4};
17constexpr ResultCode ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5};
18constexpr ResultCode ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6};
19constexpr ResultCode ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7};
20
21constexpr ResultCode ResultInvalidSize{ErrorModule::SPL, 100};
22constexpr ResultCode ResultUnknownSecureMonitorError{ErrorModule::SPL, 101};
23constexpr ResultCode ResultDecryptionFailed{ErrorModule::SPL, 102};
24
25constexpr ResultCode ResultOutOfKeySlots{ErrorModule::SPL, 104};
26constexpr ResultCode ResultInvalidKeySlot{ErrorModule::SPL, 105};
27constexpr ResultCode ResultBootReasonAlreadySet{ErrorModule::SPL, 106};
28constexpr ResultCode ResultBootReasonNotSet{ErrorModule::SPL, 107};
29constexpr ResultCode ResultInvalidArgument{ErrorModule::SPL, 108};
30
31} // namespace Service::SPL
diff --git a/src/core/hle/service/spl/spl_types.h b/src/core/hle/service/spl/spl_types.h
new file mode 100644
index 000000000..a654e7556
--- /dev/null
+++ b/src/core/hle/service/spl/spl_types.h
@@ -0,0 +1,232 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <span>
8
9#include "common/bit_field.h"
10#include "common/common_types.h"
11
12namespace Service::SPL {
13
14constexpr size_t AES_128_KEY_SIZE = 0x10;
15
16namespace Smc {
17
18enum class FunctionId : u32 {
19 SetConfig = 0xC3000401,
20 GetConfig = 0xC3000002,
21 GetResult = 0xC3000003,
22 GetResultData = 0xC3000404,
23 ModularExponentiate = 0xC3000E05,
24 GenerateRandomBytes = 0xC3000006,
25 GenerateAesKek = 0xC3000007,
26 LoadAesKey = 0xC3000008,
27 ComputeAes = 0xC3000009,
28 GenerateSpecificAesKey = 0xC300000A,
29 ComputeCmac = 0xC300040B,
30 ReencryptDeviceUniqueData = 0xC300D60C,
31 DecryptDeviceUniqueData = 0xC300100D,
32
33 ModularExponentiateWithStorageKey = 0xC300060F,
34 PrepareEsDeviceUniqueKey = 0xC3000610,
35 LoadPreparedAesKey = 0xC3000011,
36 PrepareCommonEsTitleKey = 0xC3000012,
37
38 // Deprecated functions.
39 LoadEsDeviceKey = 0xC300100C,
40 DecryptAndStoreGcKey = 0xC300100E,
41
42 // Atmosphere functions.
43 AtmosphereIramCopy = 0xF0000201,
44 AtmosphereReadWriteRegister = 0xF0000002,
45
46 AtmosphereGetEmummcConfig = 0xF0000404,
47};
48
49enum class CipherMode {
50 CbcEncrypt = 0,
51 CbcDecrypt = 1,
52 Ctr = 2,
53};
54
55enum class DeviceUniqueDataMode {
56 DecryptDeviceUniqueData = 0,
57 DecryptAndStoreGcKey = 1,
58 DecryptAndStoreEsDeviceKey = 2,
59 DecryptAndStoreSslKey = 3,
60 DecryptAndStoreDrmDeviceCertKey = 4,
61};
62
63enum class ModularExponentiateWithStorageKeyMode {
64 Gc = 0,
65 Ssl = 1,
66 DrmDeviceCert = 2,
67};
68
69enum class EsCommonKeyType {
70 TitleKey = 0,
71 ArchiveKey = 1,
72};
73
74struct AsyncOperationKey {
75 u64 value;
76};
77
78} // namespace Smc
79
80enum class HardwareType {
81 Icosa = 0,
82 Copper = 1,
83 Hoag = 2,
84 Iowa = 3,
85 Calcio = 4,
86 Aula = 5,
87};
88
89enum class SocType {
90 Erista = 0,
91 Mariko = 1,
92};
93
94enum class HardwareState {
95 Development = 0,
96 Production = 1,
97};
98
99enum class MemoryArrangement {
100 Standard = 0,
101 StandardForAppletDev = 1,
102 StandardForSystemDev = 2,
103 Expanded = 3,
104 ExpandedForAppletDev = 4,
105
106 // Note: Dynamic is not official.
107 // Atmosphere uses it to maintain compatibility with firmwares prior to 6.0.0,
108 // which removed the explicit retrieval of memory arrangement from PM.
109 Dynamic = 5,
110 Count,
111};
112
113enum class BootReason {
114 Unknown = 0,
115 AcOk = 1,
116 OnKey = 2,
117 RtcAlarm1 = 3,
118 RtcAlarm2 = 4,
119};
120
121struct BootReasonValue {
122 union {
123 u32 value{};
124
125 BitField<0, 8, u32> power_intr;
126 BitField<8, 8, u32> rtc_intr;
127 BitField<16, 8, u32> nv_erc;
128 BitField<24, 8, u32> boot_reason;
129 };
130};
131static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");
132
133struct AesKey {
134 std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};
135
136 std::span<u8> AsBytes() {
137 return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
138 }
139
140 std::span<const u8> AsBytes() const {
141 return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
142 }
143};
144static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "AesKey definition!");
145
146struct IvCtr {
147 std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};
148
149 std::span<u8> AsBytes() {
150 return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
151 }
152
153 std::span<const u8> AsBytes() const {
154 return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
155 }
156};
157static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "IvCtr definition!");
158
159struct Cmac {
160 std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};
161
162 std::span<u8> AsBytes() {
163 return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
164 }
165
166 std::span<const u8> AsBytes() const {
167 return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
168 }
169};
170static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "Cmac definition!");
171
172struct AccessKey {
173 std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};
174
175 std::span<u8> AsBytes() {
176 return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
177 }
178
179 std::span<const u8> AsBytes() const {
180 return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
181 }
182};
183static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "AccessKey definition!");
184
185struct KeySource {
186 std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};
187
188 std::span<u8> AsBytes() {
189 return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
190 }
191
192 std::span<const u8> AsBytes() const {
193 return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
194 }
195};
196static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "KeySource definition!");
197
198enum class ConfigItem : u32 {
199 // Standard config items.
200 DisableProgramVerification = 1,
201 DramId = 2,
202 SecurityEngineInterruptNumber = 3,
203 FuseVersion = 4,
204 HardwareType = 5,
205 HardwareState = 6,
206 IsRecoveryBoot = 7,
207 DeviceId = 8,
208 BootReason = 9,
209 MemoryMode = 10,
210 IsDevelopmentFunctionEnabled = 11,
211 KernelConfiguration = 12,
212 IsChargerHiZModeEnabled = 13,
213 QuestState = 14,
214 RegulatorType = 15,
215 DeviceUniqueKeyGeneration = 16,
216 Package2Hash = 17,
217
218 // Extension config items for exosphere.
219 ExosphereApiVersion = 65000,
220 ExosphereNeedsReboot = 65001,
221 ExosphereNeedsShutdown = 65002,
222 ExosphereGitCommitHash = 65003,
223 ExosphereHasRcmBugPatch = 65004,
224 ExosphereBlankProdInfo = 65005,
225 ExosphereAllowCalWrites = 65006,
226 ExosphereEmummcType = 65007,
227 ExospherePayloadAddress = 65008,
228 ExosphereLogConfiguration = 65009,
229 ExosphereForceEnableUsb30 = 65010,
230};
231
232} // namespace Service::SPL
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index bf4402308..c634b6abd 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -125,7 +125,7 @@ ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& locati
125 return ERROR_TIME_NOT_FOUND; 125 return ERROR_TIME_NOT_FOUND;
126 } 126 }
127 127
128 vfs_file = zoneinfo_dir->GetFile(location_name); 128 vfs_file = zoneinfo_dir->GetFileRelative(location_name);
129 if (!vfs_file) { 129 if (!vfs_file) {
130 LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.", 130 LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.",
131 time_zone_binary_titleid, location_name); 131 time_zone_binary_titleid, location_name);
diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp
index a335e6da1..3b052ffb2 100644
--- a/src/input_common/mouse/mouse_input.cpp
+++ b/src/input_common/mouse/mouse_input.cpp
@@ -2,25 +2,23 @@
2// Licensed under GPLv2+ 2// Licensed under GPLv2+
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <stop_token>
6#include <thread>
7
5#include "common/settings.h" 8#include "common/settings.h"
6#include "input_common/mouse/mouse_input.h" 9#include "input_common/mouse/mouse_input.h"
7 10
8namespace MouseInput { 11namespace MouseInput {
9 12
10Mouse::Mouse() { 13Mouse::Mouse() {
11 update_thread = std::thread(&Mouse::UpdateThread, this); 14 update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
12} 15}
13 16
14Mouse::~Mouse() { 17Mouse::~Mouse() = default;
15 update_thread_running = false;
16 if (update_thread.joinable()) {
17 update_thread.join();
18 }
19}
20 18
21void Mouse::UpdateThread() { 19void Mouse::UpdateThread(std::stop_token stop_token) {
22 constexpr int update_time = 10; 20 constexpr int update_time = 10;
23 while (update_thread_running) { 21 while (!stop_token.stop_requested()) {
24 for (MouseInfo& info : mouse_info) { 22 for (MouseInfo& info : mouse_info) {
25 const Common::Vec3f angular_direction{ 23 const Common::Vec3f angular_direction{
26 -info.tilt_direction.y, 24 -info.tilt_direction.y,
diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h
index 5a971ad67..c8bae99c1 100644
--- a/src/input_common/mouse/mouse_input.h
+++ b/src/input_common/mouse/mouse_input.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <mutex> 8#include <mutex>
9#include <stop_token>
9#include <thread> 10#include <thread>
10 11
11#include "common/common_types.h" 12#include "common/common_types.h"
@@ -85,7 +86,7 @@ public:
85 [[nodiscard]] const MouseData& GetMouseState(std::size_t button) const; 86 [[nodiscard]] const MouseData& GetMouseState(std::size_t button) const;
86 87
87private: 88private:
88 void UpdateThread(); 89 void UpdateThread(std::stop_token stop_token);
89 void UpdateYuzuSettings(); 90 void UpdateYuzuSettings();
90 void StopPanning(); 91 void StopPanning();
91 92
@@ -105,12 +106,11 @@ private:
105 u16 buttons{}; 106 u16 buttons{};
106 u16 toggle_buttons{}; 107 u16 toggle_buttons{};
107 u16 lock_buttons{}; 108 u16 lock_buttons{};
108 std::thread update_thread; 109 std::jthread update_thread;
109 MouseButton last_button{MouseButton::Undefined}; 110 MouseButton last_button{MouseButton::Undefined};
110 std::array<MouseInfo, 7> mouse_info; 111 std::array<MouseInfo, 7> mouse_info;
111 Common::SPSCQueue<MouseStatus> mouse_queue; 112 Common::SPSCQueue<MouseStatus> mouse_queue;
112 bool configuring{false}; 113 bool configuring{false};
113 bool update_thread_running{true};
114 int mouse_panning_timout{}; 114 int mouse_panning_timout{};
115}; 115};
116} // namespace MouseInput 116} // namespace MouseInput
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp
index d96104a4e..758f7af1f 100644
--- a/src/input_common/mouse/mouse_poller.cpp
+++ b/src/input_common/mouse/mouse_poller.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <memory>
5#include <mutex> 7#include <mutex>
6#include <utility> 8#include <utility>
7 9
diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp
index e94ba197b..5b24fd8bf 100644
--- a/src/input_common/touch_from_button.cpp
+++ b/src/input_common/touch_from_button.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include "common/settings.h" 6#include "common/settings.h"
6#include "core/frontend/framebuffer_layout.h" 7#include "core/frontend/framebuffer_layout.h"
7#include "input_common/touch_from_button.h" 8#include "input_common/touch_from_button.h"
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp
index e241f8be5..2dc7b5d5e 100644
--- a/src/tests/common/host_memory.cpp
+++ b/src/tests/common/host_memory.cpp
@@ -5,11 +5,13 @@
5#include <catch2/catch.hpp> 5#include <catch2/catch.hpp>
6 6
7#include "common/host_memory.h" 7#include "common/host_memory.h"
8#include "common/literals.h"
8 9
9using Common::HostMemory; 10using Common::HostMemory;
11using namespace Common::Literals;
10 12
11static constexpr size_t VIRTUAL_SIZE = 1ULL << 39; 13static constexpr size_t VIRTUAL_SIZE = 1ULL << 39;
12static constexpr size_t BACKING_SIZE = 4ULL * 1024 * 1024 * 1024; 14static constexpr size_t BACKING_SIZE = 4_GiB;
13 15
14TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { 16TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
15 { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); } 17 { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index f9454bbaa..e31eb30c0 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -293,6 +293,7 @@ endif()
293if (MSVC) 293if (MSVC)
294 target_compile_options(video_core PRIVATE 294 target_compile_options(video_core PRIVATE
295 /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data 295 /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data
296 /we4244 # 'var' : conversion from integer to 'type', possible loss of data
296 /we4456 # Declaration of 'identifier' hides previous local declaration 297 /we4456 # Declaration of 'identifier' hides previous local declaration
297 /we4457 # Declaration of 'identifier' hides function parameter 298 /we4457 # Declaration of 'identifier' hides function parameter
298 /we4458 # Declaration of 'identifier' hides class member 299 /we4458 # Declaration of 'identifier' hides class member
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index a39505903..b121d36a3 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -256,6 +256,16 @@ public:
256 stream_score += score; 256 stream_score += score;
257 } 257 }
258 258
259 /// Sets the new frame tick
260 void SetFrameTick(u64 new_frame_tick) noexcept {
261 frame_tick = new_frame_tick;
262 }
263
264 /// Returns the new frame tick
265 [[nodiscard]] u64 FrameTick() const noexcept {
266 return frame_tick;
267 }
268
259 /// Returns the likeliness of this being a stream buffer 269 /// Returns the likeliness of this being a stream buffer
260 [[nodiscard]] int StreamScore() const noexcept { 270 [[nodiscard]] int StreamScore() const noexcept {
261 return stream_score; 271 return stream_score;
@@ -586,6 +596,7 @@ private:
586 RasterizerInterface* rasterizer = nullptr; 596 RasterizerInterface* rasterizer = nullptr;
587 VAddr cpu_addr = 0; 597 VAddr cpu_addr = 0;
588 Words words; 598 Words words;
599 u64 frame_tick = 0;
589 BufferFlagBits flags{}; 600 BufferFlagBits flags{};
590 int stream_score = 0; 601 int stream_score = 0;
591}; 602};
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index d371b842f..cad7f902d 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -18,6 +18,7 @@
18 18
19#include "common/common_types.h" 19#include "common/common_types.h"
20#include "common/div_ceil.h" 20#include "common/div_ceil.h"
21#include "common/literals.h"
21#include "common/microprofile.h" 22#include "common/microprofile.h"
22#include "common/scope_exit.h" 23#include "common/scope_exit.h"
23#include "common/settings.h" 24#include "common/settings.h"
@@ -47,8 +48,11 @@ constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8;
47constexpr u32 NUM_STORAGE_BUFFERS = 16; 48constexpr u32 NUM_STORAGE_BUFFERS = 16;
48constexpr u32 NUM_STAGES = 5; 49constexpr u32 NUM_STAGES = 5;
49 50
51using namespace Common::Literals;
52
50template <typename P> 53template <typename P>
51class BufferCache { 54class BufferCache {
55
52 // Page size for caching purposes. 56 // Page size for caching purposes.
53 // This is unrelated to the CPU page size and it can be changed as it seems optimal. 57 // This is unrelated to the CPU page size and it can be changed as it seems optimal.
54 static constexpr u32 PAGE_BITS = 16; 58 static constexpr u32 PAGE_BITS = 16;
@@ -65,6 +69,9 @@ class BufferCache {
65 69
66 static constexpr BufferId NULL_BUFFER_ID{0}; 70 static constexpr BufferId NULL_BUFFER_ID{0};
67 71
72 static constexpr u64 EXPECTED_MEMORY = 512_MiB;
73 static constexpr u64 CRITICAL_MEMORY = 1_GiB;
74
68 using Maxwell = Tegra::Engines::Maxwell3D::Regs; 75 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
69 76
70 using Runtime = typename P::Runtime; 77 using Runtime = typename P::Runtime;
@@ -92,7 +99,7 @@ class BufferCache {
92 }; 99 };
93 100
94public: 101public:
95 static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4096; 102 static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast<u32>(4_KiB);
96 103
97 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_, 104 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
98 Tegra::Engines::Maxwell3D& maxwell3d_, 105 Tegra::Engines::Maxwell3D& maxwell3d_,
@@ -188,6 +195,8 @@ private:
188 ((cpu_addr + size) & ~Core::Memory::PAGE_MASK); 195 ((cpu_addr + size) & ~Core::Memory::PAGE_MASK);
189 } 196 }
190 197
198 void RunGarbageCollector();
199
191 void BindHostIndexBuffer(); 200 void BindHostIndexBuffer();
192 201
193 void BindHostVertexBuffers(); 202 void BindHostVertexBuffers();
@@ -243,6 +252,8 @@ private:
243 template <bool insert> 252 template <bool insert>
244 void ChangeRegister(BufferId buffer_id); 253 void ChangeRegister(BufferId buffer_id);
245 254
255 void TouchBuffer(Buffer& buffer) const noexcept;
256
246 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); 257 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
247 258
248 bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size); 259 bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
@@ -255,6 +266,10 @@ private:
255 266
256 void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies); 267 void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
257 268
269 void DownloadBufferMemory(Buffer& buffer_id);
270
271 void DownloadBufferMemory(Buffer& buffer_id, VAddr cpu_addr, u64 size);
272
258 void DeleteBuffer(BufferId buffer_id); 273 void DeleteBuffer(BufferId buffer_id);
259 274
260 void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id); 275 void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id);
@@ -319,6 +334,10 @@ private:
319 size_t immediate_buffer_capacity = 0; 334 size_t immediate_buffer_capacity = 0;
320 std::unique_ptr<u8[]> immediate_buffer_alloc; 335 std::unique_ptr<u8[]> immediate_buffer_alloc;
321 336
337 typename SlotVector<Buffer>::Iterator deletion_iterator;
338 u64 frame_tick = 0;
339 u64 total_used_memory = 0;
340
322 std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table; 341 std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table;
323}; 342};
324 343
@@ -332,6 +351,28 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
332 gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_}, runtime{runtime_} { 351 gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_}, runtime{runtime_} {
333 // Ensure the first slot is used for the null buffer 352 // Ensure the first slot is used for the null buffer
334 void(slot_buffers.insert(runtime, NullBufferParams{})); 353 void(slot_buffers.insert(runtime, NullBufferParams{}));
354 deletion_iterator = slot_buffers.end();
355}
356
357template <class P>
358void BufferCache<P>::RunGarbageCollector() {
359 const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY;
360 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
361 int num_iterations = aggressive_gc ? 64 : 32;
362 for (; num_iterations > 0; --num_iterations) {
363 if (deletion_iterator == slot_buffers.end()) {
364 deletion_iterator = slot_buffers.begin();
365 }
366 ++deletion_iterator;
367 if (deletion_iterator == slot_buffers.end()) {
368 break;
369 }
370 const auto [buffer_id, buffer] = *deletion_iterator;
371 if (buffer->FrameTick() + ticks_to_destroy < frame_tick) {
372 DownloadBufferMemory(*buffer);
373 DeleteBuffer(buffer_id);
374 }
375 }
335} 376}
336 377
337template <class P> 378template <class P>
@@ -349,6 +390,10 @@ void BufferCache<P>::TickFrame() {
349 const bool skip_preferred = hits * 256 < shots * 251; 390 const bool skip_preferred = hits * 256 < shots * 251;
350 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0; 391 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
351 392
393 if (Settings::values.use_caches_gc.GetValue() && total_used_memory >= EXPECTED_MEMORY) {
394 RunGarbageCollector();
395 }
396 ++frame_tick;
352 delayed_destruction_ring.Tick(); 397 delayed_destruction_ring.Tick();
353} 398}
354 399
@@ -372,48 +417,7 @@ void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) {
372template <class P> 417template <class P>
373void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) { 418void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) {
374 ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) { 419 ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) {
375 boost::container::small_vector<BufferCopy, 1> copies; 420 DownloadBufferMemory(buffer, cpu_addr, size);
376 u64 total_size_bytes = 0;
377 u64 largest_copy = 0;
378 buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
379 copies.push_back(BufferCopy{
380 .src_offset = range_offset,
381 .dst_offset = total_size_bytes,
382 .size = range_size,
383 });
384 total_size_bytes += range_size;
385 largest_copy = std::max(largest_copy, range_size);
386 });
387 if (total_size_bytes == 0) {
388 return;
389 }
390 MICROPROFILE_SCOPE(GPU_DownloadMemory);
391
392 if constexpr (USE_MEMORY_MAPS) {
393 auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
394 const u8* const mapped_memory = download_staging.mapped_span.data();
395 const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
396 for (BufferCopy& copy : copies) {
397 // Modify copies to have the staging offset in mind
398 copy.dst_offset += download_staging.offset;
399 }
400 runtime.CopyBuffer(download_staging.buffer, buffer, copies_span);
401 runtime.Finish();
402 for (const BufferCopy& copy : copies) {
403 const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
404 // Undo the modified offset
405 const u64 dst_offset = copy.dst_offset - download_staging.offset;
406 const u8* copy_mapped_memory = mapped_memory + dst_offset;
407 cpu_memory.WriteBlockUnsafe(copy_cpu_addr, copy_mapped_memory, copy.size);
408 }
409 } else {
410 const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
411 for (const BufferCopy& copy : copies) {
412 buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
413 const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
414 cpu_memory.WriteBlockUnsafe(copy_cpu_addr, immediate_buffer.data(), copy.size);
415 }
416 }
417 }); 421 });
418} 422}
419 423
@@ -640,6 +644,7 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
640template <class P> 644template <class P>
641void BufferCache<P>::BindHostIndexBuffer() { 645void BufferCache<P>::BindHostIndexBuffer() {
642 Buffer& buffer = slot_buffers[index_buffer.buffer_id]; 646 Buffer& buffer = slot_buffers[index_buffer.buffer_id];
647 TouchBuffer(buffer);
643 const u32 offset = buffer.Offset(index_buffer.cpu_addr); 648 const u32 offset = buffer.Offset(index_buffer.cpu_addr);
644 const u32 size = index_buffer.size; 649 const u32 size = index_buffer.size;
645 SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); 650 SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
@@ -658,6 +663,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
658 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { 663 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
659 const Binding& binding = vertex_buffers[index]; 664 const Binding& binding = vertex_buffers[index];
660 Buffer& buffer = slot_buffers[binding.buffer_id]; 665 Buffer& buffer = slot_buffers[binding.buffer_id];
666 TouchBuffer(buffer);
661 SynchronizeBuffer(buffer, binding.cpu_addr, binding.size); 667 SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
662 if (!flags[Dirty::VertexBuffer0 + index]) { 668 if (!flags[Dirty::VertexBuffer0 + index]) {
663 continue; 669 continue;
@@ -693,6 +699,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
693 const VAddr cpu_addr = binding.cpu_addr; 699 const VAddr cpu_addr = binding.cpu_addr;
694 const u32 size = binding.size; 700 const u32 size = binding.size;
695 Buffer& buffer = slot_buffers[binding.buffer_id]; 701 Buffer& buffer = slot_buffers[binding.buffer_id];
702 TouchBuffer(buffer);
696 const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID && 703 const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
697 size <= uniform_buffer_skip_cache_size && 704 size <= uniform_buffer_skip_cache_size &&
698 !buffer.IsRegionGpuModified(cpu_addr, size); 705 !buffer.IsRegionGpuModified(cpu_addr, size);
@@ -744,6 +751,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
744 ForEachEnabledBit(enabled_storage_buffers[stage], [&](u32 index) { 751 ForEachEnabledBit(enabled_storage_buffers[stage], [&](u32 index) {
745 const Binding& binding = storage_buffers[stage][index]; 752 const Binding& binding = storage_buffers[stage][index];
746 Buffer& buffer = slot_buffers[binding.buffer_id]; 753 Buffer& buffer = slot_buffers[binding.buffer_id];
754 TouchBuffer(buffer);
747 const u32 size = binding.size; 755 const u32 size = binding.size;
748 SynchronizeBuffer(buffer, binding.cpu_addr, size); 756 SynchronizeBuffer(buffer, binding.cpu_addr, size);
749 757
@@ -766,6 +774,7 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
766 for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { 774 for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
767 const Binding& binding = transform_feedback_buffers[index]; 775 const Binding& binding = transform_feedback_buffers[index];
768 Buffer& buffer = slot_buffers[binding.buffer_id]; 776 Buffer& buffer = slot_buffers[binding.buffer_id];
777 TouchBuffer(buffer);
769 const u32 size = binding.size; 778 const u32 size = binding.size;
770 SynchronizeBuffer(buffer, binding.cpu_addr, size); 779 SynchronizeBuffer(buffer, binding.cpu_addr, size);
771 780
@@ -784,6 +793,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
784 ForEachEnabledBit(enabled_compute_uniform_buffers, [&](u32 index) { 793 ForEachEnabledBit(enabled_compute_uniform_buffers, [&](u32 index) {
785 const Binding& binding = compute_uniform_buffers[index]; 794 const Binding& binding = compute_uniform_buffers[index];
786 Buffer& buffer = slot_buffers[binding.buffer_id]; 795 Buffer& buffer = slot_buffers[binding.buffer_id];
796 TouchBuffer(buffer);
787 const u32 size = binding.size; 797 const u32 size = binding.size;
788 SynchronizeBuffer(buffer, binding.cpu_addr, size); 798 SynchronizeBuffer(buffer, binding.cpu_addr, size);
789 799
@@ -803,6 +813,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
803 ForEachEnabledBit(enabled_compute_storage_buffers, [&](u32 index) { 813 ForEachEnabledBit(enabled_compute_storage_buffers, [&](u32 index) {
804 const Binding& binding = compute_storage_buffers[index]; 814 const Binding& binding = compute_storage_buffers[index];
805 Buffer& buffer = slot_buffers[binding.buffer_id]; 815 Buffer& buffer = slot_buffers[binding.buffer_id];
816 TouchBuffer(buffer);
806 const u32 size = binding.size; 817 const u32 size = binding.size;
807 SynchronizeBuffer(buffer, binding.cpu_addr, size); 818 SynchronizeBuffer(buffer, binding.cpu_addr, size);
808 819
@@ -1101,6 +1112,7 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
1101 const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size); 1112 const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size);
1102 const u32 size = static_cast<u32>(overlap.end - overlap.begin); 1113 const u32 size = static_cast<u32>(overlap.end - overlap.begin);
1103 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); 1114 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
1115 TouchBuffer(slot_buffers[new_buffer_id]);
1104 for (const BufferId overlap_id : overlap.ids) { 1116 for (const BufferId overlap_id : overlap.ids) {
1105 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); 1117 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
1106 } 1118 }
@@ -1122,8 +1134,14 @@ template <class P>
1122template <bool insert> 1134template <bool insert>
1123void BufferCache<P>::ChangeRegister(BufferId buffer_id) { 1135void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
1124 const Buffer& buffer = slot_buffers[buffer_id]; 1136 const Buffer& buffer = slot_buffers[buffer_id];
1137 const auto size = buffer.SizeBytes();
1138 if (insert) {
1139 total_used_memory += Common::AlignUp(size, 1024);
1140 } else {
1141 total_used_memory -= Common::AlignUp(size, 1024);
1142 }
1125 const VAddr cpu_addr_begin = buffer.CpuAddr(); 1143 const VAddr cpu_addr_begin = buffer.CpuAddr();
1126 const VAddr cpu_addr_end = cpu_addr_begin + buffer.SizeBytes(); 1144 const VAddr cpu_addr_end = cpu_addr_begin + size;
1127 const u64 page_begin = cpu_addr_begin / PAGE_SIZE; 1145 const u64 page_begin = cpu_addr_begin / PAGE_SIZE;
1128 const u64 page_end = Common::DivCeil(cpu_addr_end, PAGE_SIZE); 1146 const u64 page_end = Common::DivCeil(cpu_addr_end, PAGE_SIZE);
1129 for (u64 page = page_begin; page != page_end; ++page) { 1147 for (u64 page = page_begin; page != page_end; ++page) {
@@ -1136,6 +1154,11 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
1136} 1154}
1137 1155
1138template <class P> 1156template <class P>
1157void BufferCache<P>::TouchBuffer(Buffer& buffer) const noexcept {
1158 buffer.SetFrameTick(frame_tick);
1159}
1160
1161template <class P>
1139bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { 1162bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
1140 if (buffer.CpuAddr() == 0) { 1163 if (buffer.CpuAddr() == 0) {
1141 return true; 1164 return true;
@@ -1212,6 +1235,57 @@ void BufferCache<P>::MappedUploadMemory(Buffer& buffer, u64 total_size_bytes,
1212} 1235}
1213 1236
1214template <class P> 1237template <class P>
1238void BufferCache<P>::DownloadBufferMemory(Buffer& buffer) {
1239 DownloadBufferMemory(buffer, buffer.CpuAddr(), buffer.SizeBytes());
1240}
1241
1242template <class P>
1243void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 size) {
1244 boost::container::small_vector<BufferCopy, 1> copies;
1245 u64 total_size_bytes = 0;
1246 u64 largest_copy = 0;
1247 buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
1248 copies.push_back(BufferCopy{
1249 .src_offset = range_offset,
1250 .dst_offset = total_size_bytes,
1251 .size = range_size,
1252 });
1253 total_size_bytes += range_size;
1254 largest_copy = std::max(largest_copy, range_size);
1255 });
1256 if (total_size_bytes == 0) {
1257 return;
1258 }
1259 MICROPROFILE_SCOPE(GPU_DownloadMemory);
1260
1261 if constexpr (USE_MEMORY_MAPS) {
1262 auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
1263 const u8* const mapped_memory = download_staging.mapped_span.data();
1264 const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
1265 for (BufferCopy& copy : copies) {
1266 // Modify copies to have the staging offset in mind
1267 copy.dst_offset += download_staging.offset;
1268 }
1269 runtime.CopyBuffer(download_staging.buffer, buffer, copies_span);
1270 runtime.Finish();
1271 for (const BufferCopy& copy : copies) {
1272 const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
1273 // Undo the modified offset
1274 const u64 dst_offset = copy.dst_offset - download_staging.offset;
1275 const u8* copy_mapped_memory = mapped_memory + dst_offset;
1276 cpu_memory.WriteBlockUnsafe(copy_cpu_addr, copy_mapped_memory, copy.size);
1277 }
1278 } else {
1279 const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
1280 for (const BufferCopy& copy : copies) {
1281 buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
1282 const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
1283 cpu_memory.WriteBlockUnsafe(copy_cpu_addr, immediate_buffer.data(), copy.size);
1284 }
1285 }
1286}
1287
1288template <class P>
1215void BufferCache<P>::DeleteBuffer(BufferId buffer_id) { 1289void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
1216 const auto scalar_replace = [buffer_id](Binding& binding) { 1290 const auto scalar_replace = [buffer_id](Binding& binding) {
1217 if (binding.buffer_id == buffer_id) { 1291 if (binding.buffer_id == buffer_id) {
@@ -1236,6 +1310,7 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
1236 1310
1237 Unregister(buffer_id); 1311 Unregister(buffer_id);
1238 delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id])); 1312 delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
1313 slot_buffers.erase(buffer_id);
1239 1314
1240 NotifyBufferDeletion(); 1315 NotifyBufferDeletion();
1241} 1316}
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h
index 8a2a6c360..3e135a2a6 100644
--- a/src/video_core/command_classes/codecs/codec.h
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -14,10 +14,18 @@ extern "C" {
14#pragma GCC diagnostic push 14#pragma GCC diagnostic push
15#pragma GCC diagnostic ignored "-Wconversion" 15#pragma GCC diagnostic ignored "-Wconversion"
16#endif 16#endif
17#ifdef _MSC_VER
18#pragma warning(push)
19#pragma warning(disable : 4242) // conversion from 'type' to 'type', possible loss of data
20#pragma warning(disable : 4244) // conversion from 'type' to 'type', possible loss of data
21#endif
17#include <libavcodec/avcodec.h> 22#include <libavcodec/avcodec.h>
18#if defined(__GNUC__) || defined(__clang__) 23#if defined(__GNUC__) || defined(__clang__)
19#pragma GCC diagnostic pop 24#pragma GCC diagnostic pop
20#endif 25#endif
26#ifdef _MSC_VER
27#pragma warning(pop)
28#endif
21} 29}
22 30
23namespace Tegra { 31namespace Tegra {
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
index 0a8b82f2b..5faf8c0f1 100644
--- a/src/video_core/command_classes/vic.cpp
+++ b/src/video_core/command_classes/vic.cpp
@@ -3,7 +3,28 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array> 5#include <array>
6
7extern "C" {
8#if defined(__GNUC__) || defined(__clang__)
9#pragma GCC diagnostic push
10#pragma GCC diagnostic ignored "-Wconversion"
11#endif
12#ifdef _MSC_VER
13#pragma warning(disable : 4244) // conversion from 'type' to 'type', possible loss of data
14#pragma warning(push)
15#endif
16#include <libswscale/swscale.h>
17#if defined(__GNUC__) || defined(__clang__)
18#pragma GCC diagnostic pop
19#endif
20#ifdef _MSC_VER
21#pragma warning(pop)
22#endif
23}
24
6#include "common/assert.h" 25#include "common/assert.h"
26#include "common/logging/log.h"
27
7#include "video_core/command_classes/nvdec.h" 28#include "video_core/command_classes/nvdec.h"
8#include "video_core/command_classes/vic.h" 29#include "video_core/command_classes/vic.h"
9#include "video_core/engines/maxwell_3d.h" 30#include "video_core/engines/maxwell_3d.h"
@@ -11,10 +32,6 @@
11#include "video_core/memory_manager.h" 32#include "video_core/memory_manager.h"
12#include "video_core/textures/decoders.h" 33#include "video_core/textures/decoders.h"
13 34
14extern "C" {
15#include <libswscale/swscale.h>
16}
17
18namespace Tegra { 35namespace Tegra {
19 36
20Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_) 37Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_)
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index ffed42a29..335383955 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -242,6 +242,7 @@ public:
242 return 4; 242 return 4;
243 default: 243 default:
244 UNREACHABLE(); 244 UNREACHABLE();
245 return 1;
245 } 246 }
246 } 247 }
247 248
diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp
index eaba1b103..c37f15bfd 100644
--- a/src/video_core/host_shaders/astc_decoder.comp
+++ b/src/video_core/host_shaders/astc_decoder.comp
@@ -11,12 +11,8 @@
11#define UNIFORM(n) 11#define UNIFORM(n)
12#define BINDING_INPUT_BUFFER 0 12#define BINDING_INPUT_BUFFER 0
13#define BINDING_ENC_BUFFER 1 13#define BINDING_ENC_BUFFER 1
14#define BINDING_6_TO_8_BUFFER 2 14#define BINDING_SWIZZLE_BUFFER 2
15#define BINDING_7_TO_8_BUFFER 3 15#define BINDING_OUTPUT_IMAGE 3
16#define BINDING_8_TO_8_BUFFER 4
17#define BINDING_BYTE_TO_16_BUFFER 5
18#define BINDING_SWIZZLE_BUFFER 6
19#define BINDING_OUTPUT_IMAGE 7
20 16
21#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv 17#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
22 18
@@ -26,10 +22,6 @@
26#define BINDING_SWIZZLE_BUFFER 0 22#define BINDING_SWIZZLE_BUFFER 0
27#define BINDING_INPUT_BUFFER 1 23#define BINDING_INPUT_BUFFER 1
28#define BINDING_ENC_BUFFER 2 24#define BINDING_ENC_BUFFER 2
29#define BINDING_6_TO_8_BUFFER 3
30#define BINDING_7_TO_8_BUFFER 4
31#define BINDING_8_TO_8_BUFFER 5
32#define BINDING_BYTE_TO_16_BUFFER 6
33#define BINDING_OUTPUT_IMAGE 0 25#define BINDING_OUTPUT_IMAGE 0
34 26
35#endif 27#endif
@@ -76,19 +68,6 @@ layout(binding = BINDING_INPUT_BUFFER, std430) readonly buffer InputBufferU32 {
76layout(binding = BINDING_ENC_BUFFER, std430) readonly buffer EncodingsValues { 68layout(binding = BINDING_ENC_BUFFER, std430) readonly buffer EncodingsValues {
77 EncodingData encoding_values[]; 69 EncodingData encoding_values[];
78}; 70};
79// ASTC Precompiled tables
80layout(binding = BINDING_6_TO_8_BUFFER, std430) readonly buffer REPLICATE_6_BIT_TO_8 {
81 uint REPLICATE_6_BIT_TO_8_TABLE[];
82};
83layout(binding = BINDING_7_TO_8_BUFFER, std430) readonly buffer REPLICATE_7_BIT_TO_8 {
84 uint REPLICATE_7_BIT_TO_8_TABLE[];
85};
86layout(binding = BINDING_8_TO_8_BUFFER, std430) readonly buffer REPLICATE_8_BIT_TO_8 {
87 uint REPLICATE_8_BIT_TO_8_TABLE[];
88};
89layout(binding = BINDING_BYTE_TO_16_BUFFER, std430) readonly buffer REPLICATE_BYTE_TO_16 {
90 uint REPLICATE_BYTE_TO_16_TABLE[];
91};
92 71
93layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly image2DArray dest_image; 72layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly image2DArray dest_image;
94 73
@@ -139,6 +118,19 @@ const uint REPLICATE_4_BIT_TO_6_TABLE[16] =
139const uint REPLICATE_5_BIT_TO_6_TABLE[32] = 118const uint REPLICATE_5_BIT_TO_6_TABLE[32] =
140 uint[](0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 33, 35, 37, 39, 41, 43, 45, 119 uint[](0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 33, 35, 37, 39, 41, 43, 45,
141 47, 49, 51, 53, 55, 57, 59, 61, 63); 120 47, 49, 51, 53, 55, 57, 59, 61, 63);
121const uint REPLICATE_6_BIT_TO_8_TABLE[64] =
122 uint[](0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 65, 69, 73, 77, 81, 85, 89,
123 93, 97, 101, 105, 109, 113, 117, 121, 125, 130, 134, 138, 142, 146, 150, 154, 158, 162,
124 166, 170, 174, 178, 182, 186, 190, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235,
125 239, 243, 247, 251, 255);
126const uint REPLICATE_7_BIT_TO_8_TABLE[128] =
127 uint[](0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44,
128 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88,
129 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126,
130 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163,
131 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199,
132 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235,
133 237, 239, 241, 243, 245, 247, 249, 251, 253, 255);
142 134
143// Input ASTC texture globals 135// Input ASTC texture globals
144uint current_index = 0; 136uint current_index = 0;
@@ -207,8 +199,7 @@ uint Replicate(uint val, uint num_bits, uint to_bit) {
207} 199}
208 200
209uvec4 ReplicateByteTo16(uvec4 value) { 201uvec4 ReplicateByteTo16(uvec4 value) {
210 return uvec4(REPLICATE_BYTE_TO_16_TABLE[value.x], REPLICATE_BYTE_TO_16_TABLE[value.y], 202 return value * 0x101;
211 REPLICATE_BYTE_TO_16_TABLE[value.z], REPLICATE_BYTE_TO_16_TABLE[value.w]);
212} 203}
213 204
214uint ReplicateBitTo7(uint value) { 205uint ReplicateBitTo7(uint value) {
@@ -236,7 +227,7 @@ uint FastReplicateTo8(uint value, uint num_bits) {
236 case 7: 227 case 7:
237 return REPLICATE_7_BIT_TO_8_TABLE[value]; 228 return REPLICATE_7_BIT_TO_8_TABLE[value];
238 case 8: 229 case 8:
239 return REPLICATE_8_BIT_TO_8_TABLE[value]; 230 return value;
240 } 231 }
241 return Replicate(value, num_bits, 8); 232 return Replicate(value, num_bits, 8);
242} 233}
@@ -1327,6 +1318,9 @@ void main() {
1327 offset += swizzle; 1318 offset += swizzle;
1328 1319
1329 const ivec3 coord = ivec3(gl_GlobalInvocationID * uvec3(block_dims, 1)); 1320 const ivec3 coord = ivec3(gl_GlobalInvocationID * uvec3(block_dims, 1));
1321 if (any(greaterThanEqual(coord, imageSize(dest_image)))) {
1322 return;
1323 }
1330 uint block_index = 1324 uint block_index =
1331 pos.z * gl_WorkGroupSize.x * gl_WorkGroupSize.y + pos.y * gl_WorkGroupSize.x + pos.x; 1325 pos.z * gl_WorkGroupSize.x * gl_WorkGroupSize.y + pos.y * gl_WorkGroupSize.x + pos.x;
1332 1326
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index f968b5b16..07939432f 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -4,10 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
8#include <functional> 7#include <functional>
9#include <optional> 8#include <optional>
10#include <span> 9#include <span>
10#include <stop_token>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/engines/fermi_2d.h" 12#include "video_core/engines/fermi_2d.h"
13#include "video_core/gpu.h" 13#include "video_core/gpu.h"
@@ -123,7 +123,7 @@ public:
123 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {} 123 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {}
124 124
125 /// Initialize disk cached resources for the game being emulated 125 /// Initialize disk cached resources for the game being emulated
126 virtual void LoadDiskResources(u64 title_id, const std::atomic_bool& stop_loading, 126 virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
127 const DiskResourceLoadCallback& callback) {} 127 const DiskResourceLoadCallback& callback) {}
128 128
129 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. 129 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
diff --git a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
index 3e4d88c30..e8d8d2aa5 100644
--- a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
@@ -454,7 +454,7 @@ private:
454 454
455 template <typename... Args> 455 template <typename... Args>
456 void AddExpression(std::string_view text, Args&&... args) { 456 void AddExpression(std::string_view text, Args&&... args) {
457 shader_source += fmt::format(text, std::forward<Args>(args)...); 457 shader_source += fmt::format(fmt::runtime(text), std::forward<Args>(args)...);
458 } 458 }
459 459
460 template <typename... Args> 460 template <typename... Args>
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index f87bb269b..eb8bdaa85 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -351,7 +351,7 @@ void RasterizerOpenGL::SetupShaders(bool is_indexed) {
351 } 351 }
352} 352}
353 353
354void RasterizerOpenGL::LoadDiskResources(u64 title_id, const std::atomic_bool& stop_loading, 354void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
355 const VideoCore::DiskResourceLoadCallback& callback) { 355 const VideoCore::DiskResourceLoadCallback& callback) {
356 shader_cache.LoadDiskCache(title_id, stop_loading, callback); 356 shader_cache.LoadDiskCache(title_id, stop_loading, callback);
357} 357}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 76298517f..9995a563b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -94,7 +94,7 @@ public:
94 const Tegra::Engines::Fermi2D::Config& copy_config) override; 94 const Tegra::Engines::Fermi2D::Config& copy_config) override;
95 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 95 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
96 u32 pixel_stride) override; 96 u32 pixel_stride) override;
97 void LoadDiskResources(u64 title_id, const std::atomic_bool& stop_loading, 97 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
98 const VideoCore::DiskResourceLoadCallback& callback) override; 98 const VideoCore::DiskResourceLoadCallback& callback) override;
99 99
100 /// Returns true when there are commands queued to the OpenGL server. 100 /// Returns true when there are commands queued to the OpenGL server.
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 5cf7cd151..5a01c59ec 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -331,7 +331,7 @@ ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer_,
331 331
332ShaderCacheOpenGL::~ShaderCacheOpenGL() = default; 332ShaderCacheOpenGL::~ShaderCacheOpenGL() = default;
333 333
334void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop_loading, 334void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, std::stop_token stop_loading,
335 const VideoCore::DiskResourceLoadCallback& callback) { 335 const VideoCore::DiskResourceLoadCallback& callback) {
336 disk_cache.BindTitleID(title_id); 336 disk_cache.BindTitleID(title_id);
337 const std::optional transferable = disk_cache.LoadTransferable(); 337 const std::optional transferable = disk_cache.LoadTransferable();
@@ -372,7 +372,7 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop
372 const auto scope = context->Acquire(); 372 const auto scope = context->Acquire();
373 373
374 for (std::size_t i = begin; i < end; ++i) { 374 for (std::size_t i = begin; i < end; ++i) {
375 if (stop_loading) { 375 if (stop_loading.stop_requested()) {
376 return; 376 return;
377 } 377 }
378 const auto& entry = (*transferable)[i]; 378 const auto& entry = (*transferable)[i];
@@ -435,7 +435,7 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop
435 precompiled_cache_altered = true; 435 precompiled_cache_altered = true;
436 return; 436 return;
437 } 437 }
438 if (stop_loading) { 438 if (stop_loading.stop_requested()) {
439 return; 439 return;
440 } 440 }
441 441
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 2aed0697e..b30308b6f 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -127,7 +127,7 @@ public:
127 ~ShaderCacheOpenGL() override; 127 ~ShaderCacheOpenGL() override;
128 128
129 /// Loads disk cache for the current game 129 /// Loads disk cache for the current game
130 void LoadDiskCache(u64 title_id, const std::atomic_bool& stop_loading, 130 void LoadDiskCache(u64 title_id, std::stop_token stop_loading,
131 const VideoCore::DiskResourceLoadCallback& callback); 131 const VideoCore::DiskResourceLoadCallback& callback);
132 132
133 /// Gets the current specified shader stage program 133 /// Gets the current specified shader stage program
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index ac78d344c..9c28498e8 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -96,7 +96,7 @@ public:
96 // etc). 96 // etc).
97 template <typename... Args> 97 template <typename... Args>
98 void AddLine(std::string_view text, Args&&... args) { 98 void AddLine(std::string_view text, Args&&... args) {
99 AddExpression(fmt::format(text, std::forward<Args>(args)...)); 99 AddExpression(fmt::format(fmt::runtime(text), std::forward<Args>(args)...));
100 AddNewLine(); 100 AddNewLine();
101 } 101 }
102 102
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h
index 6dbb6bfba..2e67922a6 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.h
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.h
@@ -12,12 +12,15 @@
12#include <glad/glad.h> 12#include <glad/glad.h>
13 13
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/literals.h"
15#include "video_core/renderer_opengl/gl_resource_manager.h" 16#include "video_core/renderer_opengl/gl_resource_manager.h"
16 17
17namespace OpenGL { 18namespace OpenGL {
18 19
20using namespace Common::Literals;
21
19class StreamBuffer { 22class StreamBuffer {
20 static constexpr size_t STREAM_BUFFER_SIZE = 64 * 1024 * 1024; 23 static constexpr size_t STREAM_BUFFER_SIZE = 64_MiB;
21 static constexpr size_t NUM_SYNCS = 16; 24 static constexpr size_t NUM_SYNCS = 16;
22 static constexpr size_t REGION_SIZE = STREAM_BUFFER_SIZE / NUM_SYNCS; 25 static constexpr size_t REGION_SIZE = STREAM_BUFFER_SIZE / NUM_SYNCS;
23 static constexpr size_t MAX_ALIGNMENT = 256; 26 static constexpr size_t MAX_ALIGNMENT = 256;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 9b4038615..23948feed 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -737,6 +737,8 @@ Image::Image(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info_,
737 } 737 }
738} 738}
739 739
740Image::~Image() = default;
741
740void Image::UploadMemory(const ImageBufferMap& map, 742void Image::UploadMemory(const ImageBufferMap& map,
741 std::span<const VideoCommon::BufferImageCopy> copies) { 743 std::span<const VideoCommon::BufferImageCopy> copies) {
742 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, map.buffer); 744 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, map.buffer);
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index df8be12ff..25fe61566 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -143,6 +143,14 @@ public:
143 explicit Image(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, GPUVAddr gpu_addr, 143 explicit Image(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, GPUVAddr gpu_addr,
144 VAddr cpu_addr); 144 VAddr cpu_addr);
145 145
146 ~Image();
147
148 Image(const Image&) = delete;
149 Image& operator=(const Image&) = delete;
150
151 Image(Image&&) = default;
152 Image& operator=(Image&&) = default;
153
146 void UploadMemory(const ImageBufferMap& map, 154 void UploadMemory(const ImageBufferMap& map,
147 std::span<const VideoCommon::BufferImageCopy> copies); 155 std::span<const VideoCommon::BufferImageCopy> copies);
148 156
@@ -235,6 +243,7 @@ struct TextureCacheParams {
235 static constexpr bool ENABLE_VALIDATION = true; 243 static constexpr bool ENABLE_VALIDATION = true;
236 static constexpr bool FRAMEBUFFER_BLITS = true; 244 static constexpr bool FRAMEBUFFER_BLITS = true;
237 static constexpr bool HAS_EMULATED_COPIES = true; 245 static constexpr bool HAS_EMULATED_COPIES = true;
246 static constexpr bool HAS_DEVICE_MEMORY_INFO = false;
238 247
239 using Runtime = OpenGL::TextureCacheRuntime; 248 using Runtime = OpenGL::TextureCacheRuntime;
240 using Image = OpenGL::Image; 249 using Image = OpenGL::Image;
diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp
index 47fddcb6e..abaf1ee6a 100644
--- a/src/video_core/renderer_opengl/util_shaders.cpp
+++ b/src/video_core/renderer_opengl/util_shaders.cpp
@@ -69,7 +69,8 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
69 swizzle_table_buffer.Create(); 69 swizzle_table_buffer.Create();
70 astc_buffer.Create(); 70 astc_buffer.Create();
71 glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0); 71 glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
72 glNamedBufferStorage(astc_buffer.handle, sizeof(ASTC_BUFFER_DATA), &ASTC_BUFFER_DATA, 0); 72 glNamedBufferStorage(astc_buffer.handle, sizeof(ASTC_ENCODINGS_VALUES), &ASTC_ENCODINGS_VALUES,
73 0);
73} 74}
74 75
75UtilShaders::~UtilShaders() = default; 76UtilShaders::~UtilShaders() = default;
@@ -79,12 +80,6 @@ void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map,
79 static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0; 80 static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
80 static constexpr GLuint BINDING_INPUT_BUFFER = 1; 81 static constexpr GLuint BINDING_INPUT_BUFFER = 1;
81 static constexpr GLuint BINDING_ENC_BUFFER = 2; 82 static constexpr GLuint BINDING_ENC_BUFFER = 2;
82
83 static constexpr GLuint BINDING_6_TO_8_BUFFER = 3;
84 static constexpr GLuint BINDING_7_TO_8_BUFFER = 4;
85 static constexpr GLuint BINDING_8_TO_8_BUFFER = 5;
86 static constexpr GLuint BINDING_BYTE_TO_16_BUFFER = 6;
87
88 static constexpr GLuint BINDING_OUTPUT_IMAGE = 0; 83 static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
89 84
90 const Extent2D tile_size{ 85 const Extent2D tile_size{
@@ -93,21 +88,7 @@ void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map,
93 }; 88 };
94 program_manager.BindHostCompute(astc_decoder_program.handle); 89 program_manager.BindHostCompute(astc_decoder_program.handle);
95 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle); 90 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
96 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_ENC_BUFFER, astc_buffer.handle, 91 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_ENC_BUFFER, astc_buffer.handle);
97 offsetof(AstcBufferData, encoding_values),
98 sizeof(AstcBufferData::encoding_values));
99 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_6_TO_8_BUFFER, astc_buffer.handle,
100 offsetof(AstcBufferData, replicate_6_to_8),
101 sizeof(AstcBufferData::replicate_6_to_8));
102 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_7_TO_8_BUFFER, astc_buffer.handle,
103 offsetof(AstcBufferData, replicate_7_to_8),
104 sizeof(AstcBufferData::replicate_7_to_8));
105 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_8_TO_8_BUFFER, astc_buffer.handle,
106 offsetof(AstcBufferData, replicate_8_to_8),
107 sizeof(AstcBufferData::replicate_8_to_8));
108 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_BYTE_TO_16_BUFFER, astc_buffer.handle,
109 offsetof(AstcBufferData, replicate_byte_to_16),
110 sizeof(AstcBufferData::replicate_byte_to_16));
111 92
112 glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes); 93 glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
113 glUniform2ui(1, tile_size.width, tile_size.height); 94 glUniform2ui(1, tile_size.width, tile_size.height);
@@ -137,6 +118,12 @@ void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map,
137 118
138 glDispatchCompute(num_dispatches_x, num_dispatches_y, image.info.resources.layers); 119 glDispatchCompute(num_dispatches_x, num_dispatches_y, image.info.resources.layers);
139 } 120 }
121 // Precautionary barrier to ensure the compute shader is done decoding prior to texture access.
122 // GL_TEXTURE_FETCH_BARRIER_BIT and GL_SHADER_IMAGE_ACCESS_BARRIER_BIT are used in a separate
123 // glMemoryBarrier call by the texture cache runtime
124 glMemoryBarrier(GL_UNIFORM_BARRIER_BIT | GL_COMMAND_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT |
125 GL_TEXTURE_UPDATE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT |
126 GL_SHADER_STORAGE_BARRIER_BIT | GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
140 program_manager.RestoreGuestCompute(); 127 program_manager.RestoreGuestCompute();
141} 128}
142 129
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 8cb65e588..0df4e1a1c 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -55,8 +55,9 @@ size_t BytesPerIndex(VkIndexType index_type) {
55template <typename T> 55template <typename T>
56std::array<T, 6> MakeQuadIndices(u32 quad, u32 first) { 56std::array<T, 6> MakeQuadIndices(u32 quad, u32 first) {
57 std::array<T, 6> indices{0, 1, 2, 0, 2, 3}; 57 std::array<T, 6> indices{0, 1, 2, 0, 2, 3};
58 std::ranges::transform(indices, indices.begin(), 58 for (T& index : indices) {
59 [quad, first](u32 index) { return first + index + quad * 4; }); 59 index = static_cast<T>(first + index + quad * 4);
60 }
60 return indices; 61 return indices;
61} 62}
62} // Anonymous namespace 63} // Anonymous namespace
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index e11406e58..205cd3b05 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -30,19 +30,16 @@
30namespace Vulkan { 30namespace Vulkan {
31 31
32using Tegra::Texture::SWIZZLE_TABLE; 32using Tegra::Texture::SWIZZLE_TABLE;
33using Tegra::Texture::ASTC::EncodingsValues; 33using Tegra::Texture::ASTC::ASTC_ENCODINGS_VALUES;
34using namespace Tegra::Texture::ASTC; 34using namespace Tegra::Texture::ASTC;
35 35
36namespace { 36namespace {
37 37
38constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0; 38constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0;
39constexpr u32 ASTC_BINDING_ENC_BUFFER = 1; 39constexpr u32 ASTC_BINDING_ENC_BUFFER = 1;
40constexpr u32 ASTC_BINDING_6_TO_8_BUFFER = 2; 40constexpr u32 ASTC_BINDING_SWIZZLE_BUFFER = 2;
41constexpr u32 ASTC_BINDING_7_TO_8_BUFFER = 3; 41constexpr u32 ASTC_BINDING_OUTPUT_IMAGE = 3;
42constexpr u32 ASTC_BINDING_8_TO_8_BUFFER = 4; 42constexpr size_t ASTC_NUM_BINDINGS = 4;
43constexpr u32 ASTC_BINDING_BYTE_TO_16_BUFFER = 5;
44constexpr u32 ASTC_BINDING_SWIZZLE_BUFFER = 6;
45constexpr u32 ASTC_BINDING_OUTPUT_IMAGE = 7;
46 43
47VkPushConstantRange BuildComputePushConstantRange(std::size_t size) { 44VkPushConstantRange BuildComputePushConstantRange(std::size_t size) {
48 return { 45 return {
@@ -71,7 +68,7 @@ std::array<VkDescriptorSetLayoutBinding, 2> BuildInputOutputDescriptorSetBinding
71 }}; 68 }};
72} 69}
73 70
74std::array<VkDescriptorSetLayoutBinding, 8> BuildASTCDescriptorSetBindings() { 71std::array<VkDescriptorSetLayoutBinding, ASTC_NUM_BINDINGS> BuildASTCDescriptorSetBindings() {
75 return {{ 72 return {{
76 { 73 {
77 .binding = ASTC_BINDING_INPUT_BUFFER, 74 .binding = ASTC_BINDING_INPUT_BUFFER,
@@ -88,34 +85,6 @@ std::array<VkDescriptorSetLayoutBinding, 8> BuildASTCDescriptorSetBindings() {
88 .pImmutableSamplers = nullptr, 85 .pImmutableSamplers = nullptr,
89 }, 86 },
90 { 87 {
91 .binding = ASTC_BINDING_6_TO_8_BUFFER,
92 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
93 .descriptorCount = 1,
94 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
95 .pImmutableSamplers = nullptr,
96 },
97 {
98 .binding = ASTC_BINDING_7_TO_8_BUFFER,
99 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
100 .descriptorCount = 1,
101 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
102 .pImmutableSamplers = nullptr,
103 },
104 {
105 .binding = ASTC_BINDING_8_TO_8_BUFFER,
106 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
107 .descriptorCount = 1,
108 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
109 .pImmutableSamplers = nullptr,
110 },
111 {
112 .binding = ASTC_BINDING_BYTE_TO_16_BUFFER,
113 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
114 .descriptorCount = 1,
115 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
116 .pImmutableSamplers = nullptr,
117 },
118 {
119 .binding = ASTC_BINDING_SWIZZLE_BUFFER, 88 .binding = ASTC_BINDING_SWIZZLE_BUFFER,
120 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 89 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
121 .descriptorCount = 1, 90 .descriptorCount = 1,
@@ -143,7 +112,8 @@ VkDescriptorUpdateTemplateEntryKHR BuildInputOutputDescriptorUpdateTemplate() {
143 }; 112 };
144} 113}
145 114
146std::array<VkDescriptorUpdateTemplateEntryKHR, 8> BuildASTCPassDescriptorUpdateTemplateEntry() { 115std::array<VkDescriptorUpdateTemplateEntryKHR, ASTC_NUM_BINDINGS>
116BuildASTCPassDescriptorUpdateTemplateEntry() {
147 return {{ 117 return {{
148 { 118 {
149 .dstBinding = ASTC_BINDING_INPUT_BUFFER, 119 .dstBinding = ASTC_BINDING_INPUT_BUFFER,
@@ -162,38 +132,6 @@ std::array<VkDescriptorUpdateTemplateEntryKHR, 8> BuildASTCPassDescriptorUpdateT
162 .stride = sizeof(DescriptorUpdateEntry), 132 .stride = sizeof(DescriptorUpdateEntry),
163 }, 133 },
164 { 134 {
165 .dstBinding = ASTC_BINDING_6_TO_8_BUFFER,
166 .dstArrayElement = 0,
167 .descriptorCount = 1,
168 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
169 .offset = ASTC_BINDING_6_TO_8_BUFFER * sizeof(DescriptorUpdateEntry),
170 .stride = sizeof(DescriptorUpdateEntry),
171 },
172 {
173 .dstBinding = ASTC_BINDING_7_TO_8_BUFFER,
174 .dstArrayElement = 0,
175 .descriptorCount = 1,
176 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
177 .offset = ASTC_BINDING_7_TO_8_BUFFER * sizeof(DescriptorUpdateEntry),
178 .stride = sizeof(DescriptorUpdateEntry),
179 },
180 {
181 .dstBinding = ASTC_BINDING_8_TO_8_BUFFER,
182 .dstArrayElement = 0,
183 .descriptorCount = 1,
184 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
185 .offset = ASTC_BINDING_8_TO_8_BUFFER * sizeof(DescriptorUpdateEntry),
186 .stride = sizeof(DescriptorUpdateEntry),
187 },
188 {
189 .dstBinding = ASTC_BINDING_BYTE_TO_16_BUFFER,
190 .dstArrayElement = 0,
191 .descriptorCount = 1,
192 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
193 .offset = ASTC_BINDING_BYTE_TO_16_BUFFER * sizeof(DescriptorUpdateEntry),
194 .stride = sizeof(DescriptorUpdateEntry),
195 },
196 {
197 .dstBinding = ASTC_BINDING_SWIZZLE_BUFFER, 135 .dstBinding = ASTC_BINDING_SWIZZLE_BUFFER,
198 .dstArrayElement = 0, 136 .dstArrayElement = 0,
199 .descriptorCount = 1, 137 .descriptorCount = 1,
@@ -222,15 +160,6 @@ struct AstcPushConstants {
222 u32 block_height_mask; 160 u32 block_height_mask;
223}; 161};
224 162
225struct AstcBufferData {
226 decltype(SWIZZLE_TABLE) swizzle_table_buffer = SWIZZLE_TABLE;
227 decltype(EncodingsValues) encoding_values = EncodingsValues;
228 decltype(REPLICATE_6_BIT_TO_8_TABLE) replicate_6_to_8 = REPLICATE_6_BIT_TO_8_TABLE;
229 decltype(REPLICATE_7_BIT_TO_8_TABLE) replicate_7_to_8 = REPLICATE_7_BIT_TO_8_TABLE;
230 decltype(REPLICATE_8_BIT_TO_8_TABLE) replicate_8_to_8 = REPLICATE_8_BIT_TO_8_TABLE;
231 decltype(REPLICATE_BYTE_TO_16_TABLE) replicate_byte_to_16 = REPLICATE_BYTE_TO_16_TABLE;
232} constexpr ASTC_BUFFER_DATA;
233
234} // Anonymous namespace 163} // Anonymous namespace
235 164
236VKComputePass::VKComputePass(const Device& device, VKDescriptorPool& descriptor_pool, 165VKComputePass::VKComputePass(const Device& device, VKDescriptorPool& descriptor_pool,
@@ -423,7 +352,7 @@ ASTCDecoderPass::ASTCDecoderPass(const Device& device_, VKScheduler& scheduler_,
423ASTCDecoderPass::~ASTCDecoderPass() = default; 352ASTCDecoderPass::~ASTCDecoderPass() = default;
424 353
425void ASTCDecoderPass::MakeDataBuffer() { 354void ASTCDecoderPass::MakeDataBuffer() {
426 constexpr size_t TOTAL_BUFFER_SIZE = sizeof(ASTC_BUFFER_DATA) + sizeof(SWIZZLE_TABLE); 355 constexpr size_t TOTAL_BUFFER_SIZE = sizeof(ASTC_ENCODINGS_VALUES) + sizeof(SWIZZLE_TABLE);
427 data_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ 356 data_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
428 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 357 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
429 .pNext = nullptr, 358 .pNext = nullptr,
@@ -437,9 +366,10 @@ void ASTCDecoderPass::MakeDataBuffer() {
437 data_buffer_commit = memory_allocator.Commit(data_buffer, MemoryUsage::Upload); 366 data_buffer_commit = memory_allocator.Commit(data_buffer, MemoryUsage::Upload);
438 367
439 const auto staging_ref = staging_buffer_pool.Request(TOTAL_BUFFER_SIZE, MemoryUsage::Upload); 368 const auto staging_ref = staging_buffer_pool.Request(TOTAL_BUFFER_SIZE, MemoryUsage::Upload);
440 std::memcpy(staging_ref.mapped_span.data(), &ASTC_BUFFER_DATA, sizeof(ASTC_BUFFER_DATA)); 369 std::memcpy(staging_ref.mapped_span.data(), &ASTC_ENCODINGS_VALUES,
370 sizeof(ASTC_ENCODINGS_VALUES));
441 // Tack on the swizzle table at the end of the buffer 371 // Tack on the swizzle table at the end of the buffer
442 std::memcpy(staging_ref.mapped_span.data() + sizeof(ASTC_BUFFER_DATA), &SWIZZLE_TABLE, 372 std::memcpy(staging_ref.mapped_span.data() + sizeof(ASTC_ENCODINGS_VALUES), &SWIZZLE_TABLE,
443 sizeof(SWIZZLE_TABLE)); 373 sizeof(SWIZZLE_TABLE));
444 374
445 scheduler.Record([src = staging_ref.buffer, offset = staging_ref.offset, dst = *data_buffer, 375 scheduler.Record([src = staging_ref.buffer, offset = staging_ref.offset, dst = *data_buffer,
@@ -509,18 +439,8 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
509 update_descriptor_queue.Acquire(); 439 update_descriptor_queue.Acquire();
510 update_descriptor_queue.AddBuffer(map.buffer, input_offset, 440 update_descriptor_queue.AddBuffer(map.buffer, input_offset,
511 image.guest_size_bytes - swizzle.buffer_offset); 441 image.guest_size_bytes - swizzle.buffer_offset);
512 update_descriptor_queue.AddBuffer(*data_buffer, offsetof(AstcBufferData, encoding_values), 442 update_descriptor_queue.AddBuffer(*data_buffer, 0, sizeof(ASTC_ENCODINGS_VALUES));
513 sizeof(AstcBufferData::encoding_values)); 443 update_descriptor_queue.AddBuffer(*data_buffer, sizeof(ASTC_ENCODINGS_VALUES),
514 update_descriptor_queue.AddBuffer(*data_buffer, offsetof(AstcBufferData, replicate_6_to_8),
515 sizeof(AstcBufferData::replicate_6_to_8));
516 update_descriptor_queue.AddBuffer(*data_buffer, offsetof(AstcBufferData, replicate_7_to_8),
517 sizeof(AstcBufferData::replicate_7_to_8));
518 update_descriptor_queue.AddBuffer(*data_buffer, offsetof(AstcBufferData, replicate_8_to_8),
519 sizeof(AstcBufferData::replicate_8_to_8));
520 update_descriptor_queue.AddBuffer(*data_buffer,
521 offsetof(AstcBufferData, replicate_byte_to_16),
522 sizeof(AstcBufferData::replicate_byte_to_16));
523 update_descriptor_queue.AddBuffer(*data_buffer, sizeof(AstcBufferData),
524 sizeof(SWIZZLE_TABLE)); 444 sizeof(SWIZZLE_TABLE));
525 update_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); 445 update_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
526 446
@@ -569,6 +489,7 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
569 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 489 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
570 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, image_barrier); 490 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, image_barrier);
571 }); 491 });
492 scheduler.Finish();
572} 493}
573 494
574} // namespace Vulkan 495} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index db78ce3d9..6852c11b0 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -2,8 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <atomic> 5#include <thread>
6#include <chrono>
7 6
8#include "common/settings.h" 7#include "common/settings.h"
9#include "video_core/renderer_vulkan/vk_master_semaphore.h" 8#include "video_core/renderer_vulkan/vk_master_semaphore.h"
@@ -12,8 +11,6 @@
12 11
13namespace Vulkan { 12namespace Vulkan {
14 13
15using namespace std::chrono_literals;
16
17MasterSemaphore::MasterSemaphore(const Device& device) { 14MasterSemaphore::MasterSemaphore(const Device& device) {
18 static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{ 15 static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{
19 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR, 16 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR,
@@ -34,9 +31,9 @@ MasterSemaphore::MasterSemaphore(const Device& device) {
34 // Validation layers have a bug where they fail to track resource usage when using timeline 31 // Validation layers have a bug where they fail to track resource usage when using timeline
35 // semaphores and synchronizing with GetSemaphoreCounterValueKHR. To workaround this issue, have 32 // semaphores and synchronizing with GetSemaphoreCounterValueKHR. To workaround this issue, have
36 // a separate thread waiting for each timeline semaphore value. 33 // a separate thread waiting for each timeline semaphore value.
37 debug_thread = std::thread([this] { 34 debug_thread = std::jthread([this](std::stop_token stop_token) {
38 u64 counter = 0; 35 u64 counter = 0;
39 while (!shutdown) { 36 while (!stop_token.stop_requested()) {
40 if (semaphore.Wait(counter, 10'000'000)) { 37 if (semaphore.Wait(counter, 10'000'000)) {
41 ++counter; 38 ++counter;
42 } 39 }
@@ -44,13 +41,6 @@ MasterSemaphore::MasterSemaphore(const Device& device) {
44 }); 41 });
45} 42}
46 43
47MasterSemaphore::~MasterSemaphore() { 44MasterSemaphore::~MasterSemaphore() = default;
48 shutdown = true;
49
50 // This thread might not be started
51 if (debug_thread.joinable()) {
52 debug_thread.join();
53 }
54}
55 45
56} // namespace Vulkan 46} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 4b6d64daa..ee3cd35d0 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -65,11 +65,10 @@ public:
65 } 65 }
66 66
67private: 67private:
68 vk::Semaphore semaphore; ///< Timeline semaphore. 68 vk::Semaphore semaphore; ///< Timeline semaphore.
69 std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick. 69 std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick.
70 std::atomic<u64> current_tick{1}; ///< Current logical tick. 70 std::atomic<u64> current_tick{1}; ///< Current logical tick.
71 std::atomic<bool> shutdown{false}; ///< True when the object is being destroyed. 71 std::jthread debug_thread; ///< Debug thread to workaround validation layer bugs.
72 std::thread debug_thread; ///< Debug thread to workaround validation layer bugs.
73}; 72};
74 73
75} // namespace Vulkan 74} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 7a1232497..0412b5234 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -12,6 +12,7 @@
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/bit_util.h" 13#include "common/bit_util.h"
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/literals.h"
15#include "video_core/renderer_vulkan/vk_scheduler.h" 16#include "video_core/renderer_vulkan/vk_scheduler.h"
16#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 17#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
17#include "video_core/vulkan_common/vulkan_device.h" 18#include "video_core/vulkan_common/vulkan_device.h"
@@ -19,12 +20,15 @@
19 20
20namespace Vulkan { 21namespace Vulkan {
21namespace { 22namespace {
23
24using namespace Common::Literals;
25
22// Maximum potential alignment of a Vulkan buffer 26// Maximum potential alignment of a Vulkan buffer
23constexpr VkDeviceSize MAX_ALIGNMENT = 256; 27constexpr VkDeviceSize MAX_ALIGNMENT = 256;
24// Maximum size to put elements in the stream buffer 28// Maximum size to put elements in the stream buffer
25constexpr VkDeviceSize MAX_STREAM_BUFFER_REQUEST_SIZE = 8 * 1024 * 1024; 29constexpr VkDeviceSize MAX_STREAM_BUFFER_REQUEST_SIZE = 8_MiB;
26// Stream buffer size in bytes 30// Stream buffer size in bytes
27constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128 * 1024 * 1024; 31constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB;
28constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS; 32constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS;
29 33
30constexpr VkMemoryPropertyFlags HOST_FLAGS = 34constexpr VkMemoryPropertyFlags HOST_FLAGS =
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
index a09fe084e..7b4875d0e 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
@@ -10,6 +10,7 @@
10 10
11#include "common/alignment.h" 11#include "common/alignment.h"
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/literals.h"
13#include "video_core/renderer_vulkan/vk_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
14#include "video_core/renderer_vulkan/vk_stream_buffer.h" 15#include "video_core/renderer_vulkan/vk_stream_buffer.h"
15#include "video_core/vulkan_common/vulkan_device.h" 16#include "video_core/vulkan_common/vulkan_device.h"
@@ -19,6 +20,8 @@ namespace Vulkan {
19 20
20namespace { 21namespace {
21 22
23using namespace Common::Literals;
24
22constexpr VkBufferUsageFlags BUFFER_USAGE = 25constexpr VkBufferUsageFlags BUFFER_USAGE =
23 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | 26 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
24 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; 27 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
@@ -26,7 +29,7 @@ constexpr VkBufferUsageFlags BUFFER_USAGE =
26constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000; 29constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000;
27constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000; 30constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000;
28 31
29constexpr u64 PREFERRED_STREAM_BUFFER_SIZE = 256 * 1024 * 1024; 32constexpr u64 PREFERRED_STREAM_BUFFER_SIZE = 256_MiB;
30 33
31/// Find a memory type with the passed requirements 34/// Find a memory type with the passed requirements
32std::optional<u32> FindMemoryType(const VkPhysicalDeviceMemoryProperties& properties, 35std::optional<u32> FindMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 52860b4cf..a2ab4d1ee 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -818,6 +818,10 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
818 }); 818 });
819} 819}
820 820
821u64 TextureCacheRuntime::GetDeviceLocalMemory() const {
822 return device.GetDeviceLocalMemory();
823}
824
821Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_addr_, 825Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_addr_,
822 VAddr cpu_addr_) 826 VAddr cpu_addr_)
823 : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime.scheduler}, 827 : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime.scheduler},
@@ -876,6 +880,8 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_
876 } 880 }
877} 881}
878 882
883Image::~Image() = default;
884
879void Image::UploadMemory(const StagingBufferRef& map, std::span<const BufferImageCopy> copies) { 885void Image::UploadMemory(const StagingBufferRef& map, std::span<const BufferImageCopy> copies) {
880 // TODO: Move this to another API 886 // TODO: Move this to another API
881 scheduler->RequestOutsideRenderPassOperationContext(); 887 scheduler->RequestOutsideRenderPassOperationContext();
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 4a57d378b..172bcdf98 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -97,6 +97,8 @@ struct TextureCacheRuntime {
97 // All known Vulkan drivers can natively handle BGR textures 97 // All known Vulkan drivers can natively handle BGR textures
98 return true; 98 return true;
99 } 99 }
100
101 u64 GetDeviceLocalMemory() const;
100}; 102};
101 103
102class Image : public VideoCommon::ImageBase { 104class Image : public VideoCommon::ImageBase {
@@ -104,6 +106,14 @@ public:
104 explicit Image(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, GPUVAddr gpu_addr, 106 explicit Image(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, GPUVAddr gpu_addr,
105 VAddr cpu_addr); 107 VAddr cpu_addr);
106 108
109 ~Image();
110
111 Image(const Image&) = delete;
112 Image& operator=(const Image&) = delete;
113
114 Image(Image&&) = default;
115 Image& operator=(Image&&) = default;
116
107 void UploadMemory(const StagingBufferRef& map, 117 void UploadMemory(const StagingBufferRef& map,
108 std::span<const VideoCommon::BufferImageCopy> copies); 118 std::span<const VideoCommon::BufferImageCopy> copies);
109 119
@@ -257,6 +267,7 @@ struct TextureCacheParams {
257 static constexpr bool ENABLE_VALIDATION = true; 267 static constexpr bool ENABLE_VALIDATION = true;
258 static constexpr bool FRAMEBUFFER_BLITS = false; 268 static constexpr bool FRAMEBUFFER_BLITS = false;
259 static constexpr bool HAS_EMULATED_COPIES = false; 269 static constexpr bool HAS_EMULATED_COPIES = false;
270 static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
260 271
261 using Runtime = Vulkan::TextureCacheRuntime; 272 using Runtime = Vulkan::TextureCacheRuntime;
262 using Image = Vulkan::Image; 273 using Image = Vulkan::Image;
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 6308aef94..eb1746265 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -283,4 +283,11 @@ std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
283 return {DefaultBlockWidth(format), DefaultBlockHeight(format)}; 283 return {DefaultBlockWidth(format), DefaultBlockHeight(format)};
284} 284}
285 285
286u64 EstimatedDecompressedSize(u64 base_size, PixelFormat format) {
287 constexpr u64 RGBA8_PIXEL_SIZE = 4;
288 const u64 base_block_size = static_cast<u64>(DefaultBlockWidth(format)) *
289 static_cast<u64>(DefaultBlockHeight(format)) * RGBA8_PIXEL_SIZE;
290 return (base_size * base_block_size) / BytesPerBlock(format);
291}
292
286} // namespace VideoCore::Surface 293} // namespace VideoCore::Surface
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index c40ab89d0..1503db81f 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -462,4 +462,6 @@ bool IsPixelFormatSRGB(PixelFormat format);
462 462
463std::pair<u32, u32> GetASTCBlockSize(PixelFormat format); 463std::pair<u32, u32> GetASTCBlockSize(PixelFormat format);
464 464
465u64 EstimatedDecompressedSize(u64 base_size, PixelFormat format);
466
465} // namespace VideoCore::Surface 467} // namespace VideoCore::Surface
diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp
index 9914926b3..ad69d32d1 100644
--- a/src/video_core/texture_cache/image_base.cpp
+++ b/src/video_core/texture_cache/image_base.cpp
@@ -113,6 +113,43 @@ void ImageBase::InsertView(const ImageViewInfo& view_info, ImageViewId image_vie
113 image_view_ids.push_back(image_view_id); 113 image_view_ids.push_back(image_view_id);
114} 114}
115 115
116bool ImageBase::IsSafeDownload() const noexcept {
117 // Skip images that were not modified from the GPU
118 if (False(flags & ImageFlagBits::GpuModified)) {
119 return false;
120 }
121 // Skip images that .are. modified from the CPU
122 // We don't want to write sensitive data from the guest
123 if (True(flags & ImageFlagBits::CpuModified)) {
124 return false;
125 }
126 if (info.num_samples > 1) {
127 LOG_WARNING(HW_GPU, "MSAA image downloads are not implemented");
128 return false;
129 }
130 return true;
131}
132
133void ImageBase::CheckBadOverlapState() {
134 if (False(flags & ImageFlagBits::BadOverlap)) {
135 return;
136 }
137 if (!overlapping_images.empty()) {
138 return;
139 }
140 flags &= ~ImageFlagBits::BadOverlap;
141}
142
143void ImageBase::CheckAliasState() {
144 if (False(flags & ImageFlagBits::Alias)) {
145 return;
146 }
147 if (!aliased_images.empty()) {
148 return;
149 }
150 flags &= ~ImageFlagBits::Alias;
151}
152
116void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_id) { 153void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_id) {
117 static constexpr auto OPTIONS = RelaxedOptions::Size | RelaxedOptions::Format; 154 static constexpr auto OPTIONS = RelaxedOptions::Size | RelaxedOptions::Format;
118 ASSERT(lhs.info.type == rhs.info.type); 155 ASSERT(lhs.info.type == rhs.info.type);
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index b7f3b7e43..e326cab71 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -25,6 +25,12 @@ enum class ImageFlagBits : u32 {
25 Strong = 1 << 5, ///< Exists in the image table, the dimensions are can be trusted 25 Strong = 1 << 5, ///< Exists in the image table, the dimensions are can be trusted
26 Registered = 1 << 6, ///< True when the image is registered 26 Registered = 1 << 6, ///< True when the image is registered
27 Picked = 1 << 7, ///< Temporary flag to mark the image as picked 27 Picked = 1 << 7, ///< Temporary flag to mark the image as picked
28
29 // Garbage Collection Flags
30 BadOverlap = 1 << 8, ///< This image overlaps other but doesn't fit, has higher
31 ///< garbage collection priority
32 Alias = 1 << 9, ///< This image has aliases and has priority on garbage
33 ///< collection
28}; 34};
29DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) 35DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
30 36
@@ -44,11 +50,16 @@ struct ImageBase {
44 50
45 void InsertView(const ImageViewInfo& view_info, ImageViewId image_view_id); 51 void InsertView(const ImageViewInfo& view_info, ImageViewId image_view_id);
46 52
53 [[nodiscard]] bool IsSafeDownload() const noexcept;
54
47 [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept { 55 [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept {
48 const VAddr overlap_end = overlap_cpu_addr + overlap_size; 56 const VAddr overlap_end = overlap_cpu_addr + overlap_size;
49 return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end; 57 return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end;
50 } 58 }
51 59
60 void CheckBadOverlapState();
61 void CheckAliasState();
62
52 ImageInfo info; 63 ImageInfo info;
53 64
54 u32 guest_size_bytes = 0; 65 u32 guest_size_bytes = 0;
@@ -72,6 +83,7 @@ struct ImageBase {
72 std::vector<SubresourceBase> slice_subresources; 83 std::vector<SubresourceBase> slice_subresources;
73 84
74 std::vector<AliasedImage> aliased_images; 85 std::vector<AliasedImage> aliased_images;
86 std::vector<ImageId> overlapping_images;
75}; 87};
76 88
77struct ImageAllocBase { 89struct ImageAllocBase {
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h
index eae3be6ea..6180b8c0e 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/video_core/texture_cache/slot_vector.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <bit>
8#include <concepts> 9#include <concepts>
9#include <numeric> 10#include <numeric>
10#include <type_traits> 11#include <type_traits>
@@ -32,6 +33,60 @@ template <class T>
32requires std::is_nothrow_move_assignable_v<T>&& 33requires std::is_nothrow_move_assignable_v<T>&&
33 std::is_nothrow_move_constructible_v<T> class SlotVector { 34 std::is_nothrow_move_constructible_v<T> class SlotVector {
34public: 35public:
36 class Iterator {
37 friend SlotVector<T>;
38
39 public:
40 constexpr Iterator() = default;
41
42 Iterator& operator++() noexcept {
43 const u64* const bitset = slot_vector->stored_bitset.data();
44 const u32 size = static_cast<u32>(slot_vector->stored_bitset.size()) * 64;
45 if (id.index < size) {
46 do {
47 ++id.index;
48 } while (id.index < size && !IsValid(bitset));
49 if (id.index == size) {
50 id.index = SlotId::INVALID_INDEX;
51 }
52 }
53 return *this;
54 }
55
56 Iterator operator++(int) noexcept {
57 const Iterator copy{*this};
58 ++*this;
59 return copy;
60 }
61
62 bool operator==(const Iterator& other) const noexcept {
63 return id.index == other.id.index;
64 }
65
66 bool operator!=(const Iterator& other) const noexcept {
67 return id.index != other.id.index;
68 }
69
70 std::pair<SlotId, T*> operator*() const noexcept {
71 return {id, std::addressof((*slot_vector)[id])};
72 }
73
74 T* operator->() const noexcept {
75 return std::addressof((*slot_vector)[id]);
76 }
77
78 private:
79 Iterator(SlotVector<T>* slot_vector_, SlotId id_) noexcept
80 : slot_vector{slot_vector_}, id{id_} {}
81
82 bool IsValid(const u64* bitset) const noexcept {
83 return ((bitset[id.index / 64] >> (id.index % 64)) & 1) != 0;
84 }
85
86 SlotVector<T>* slot_vector;
87 SlotId id;
88 };
89
35 ~SlotVector() noexcept { 90 ~SlotVector() noexcept {
36 size_t index = 0; 91 size_t index = 0;
37 for (u64 bits : stored_bitset) { 92 for (u64 bits : stored_bitset) {
@@ -70,6 +125,20 @@ public:
70 ResetStorageBit(id.index); 125 ResetStorageBit(id.index);
71 } 126 }
72 127
128 [[nodiscard]] Iterator begin() noexcept {
129 const auto it = std::ranges::find_if(stored_bitset, [](u64 value) { return value != 0; });
130 if (it == stored_bitset.end()) {
131 return end();
132 }
133 const u32 word_index = static_cast<u32>(std::distance(it, stored_bitset.begin()));
134 const SlotId first_id{word_index * 64 + static_cast<u32>(std::countr_zero(*it))};
135 return Iterator(this, first_id);
136 }
137
138 [[nodiscard]] Iterator end() noexcept {
139 return Iterator(this, SlotId{SlotId::INVALID_INDEX});
140 }
141
73private: 142private:
74 struct NonTrivialDummy { 143 struct NonTrivialDummy {
75 NonTrivialDummy() noexcept {} 144 NonTrivialDummy() noexcept {}
@@ -140,7 +209,6 @@ private:
140 209
141 Entry* values = nullptr; 210 Entry* values = nullptr;
142 size_t values_capacity = 0; 211 size_t values_capacity = 0;
143 size_t values_size = 0;
144 212
145 std::vector<u64> stored_bitset; 213 std::vector<u64> stored_bitset;
146 std::vector<u32> free_list; 214 std::vector<u32> free_list;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 59b7c678b..c7cfd02b6 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -19,9 +19,10 @@
19#include <boost/container/small_vector.hpp> 19#include <boost/container/small_vector.hpp>
20 20
21#include "common/alignment.h" 21#include "common/alignment.h"
22#include "common/common_funcs.h"
23#include "common/common_types.h" 22#include "common/common_types.h"
23#include "common/literals.h"
24#include "common/logging/log.h" 24#include "common/logging/log.h"
25#include "common/settings.h"
25#include "video_core/compatible_formats.h" 26#include "video_core/compatible_formats.h"
26#include "video_core/delayed_destruction_ring.h" 27#include "video_core/delayed_destruction_ring.h"
27#include "video_core/dirty_flags.h" 28#include "video_core/dirty_flags.h"
@@ -57,6 +58,7 @@ using VideoCore::Surface::PixelFormat;
57using VideoCore::Surface::PixelFormatFromDepthFormat; 58using VideoCore::Surface::PixelFormatFromDepthFormat;
58using VideoCore::Surface::PixelFormatFromRenderTargetFormat; 59using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
59using VideoCore::Surface::SurfaceType; 60using VideoCore::Surface::SurfaceType;
61using namespace Common::Literals;
60 62
61template <class P> 63template <class P>
62class TextureCache { 64class TextureCache {
@@ -69,12 +71,17 @@ class TextureCache {
69 static constexpr bool FRAMEBUFFER_BLITS = P::FRAMEBUFFER_BLITS; 71 static constexpr bool FRAMEBUFFER_BLITS = P::FRAMEBUFFER_BLITS;
70 /// True when some copies have to be emulated 72 /// True when some copies have to be emulated
71 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES; 73 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES;
74 /// True when the API can provide info about the memory of the device.
75 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
72 76
73 /// Image view ID for null descriptors 77 /// Image view ID for null descriptors
74 static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0}; 78 static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0};
75 /// Sampler ID for bugged sampler ids 79 /// Sampler ID for bugged sampler ids
76 static constexpr SamplerId NULL_SAMPLER_ID{0}; 80 static constexpr SamplerId NULL_SAMPLER_ID{0};
77 81
82 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB;
83 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB;
84
78 using Runtime = typename P::Runtime; 85 using Runtime = typename P::Runtime;
79 using Image = typename P::Image; 86 using Image = typename P::Image;
80 using ImageAlloc = typename P::ImageAlloc; 87 using ImageAlloc = typename P::ImageAlloc;
@@ -197,6 +204,9 @@ private:
197 } 204 }
198 } 205 }
199 206
207 /// Runs the Garbage Collector.
208 void RunGarbageCollector();
209
200 /// Fills image_view_ids in the image views in indices 210 /// Fills image_view_ids in the image views in indices
201 void FillImageViews(DescriptorTable<TICEntry>& table, 211 void FillImageViews(DescriptorTable<TICEntry>& table,
202 std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices, 212 std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices,
@@ -333,6 +343,10 @@ private:
333 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> page_table; 343 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> page_table;
334 344
335 bool has_deleted_images = false; 345 bool has_deleted_images = false;
346 u64 total_used_memory = 0;
347 u64 minimum_memory;
348 u64 expected_memory;
349 u64 critical_memory;
336 350
337 SlotVector<Image> slot_images; 351 SlotVector<Image> slot_images;
338 SlotVector<ImageView> slot_image_views; 352 SlotVector<ImageView> slot_image_views;
@@ -353,6 +367,7 @@ private:
353 367
354 u64 modification_tick = 0; 368 u64 modification_tick = 0;
355 u64 frame_tick = 0; 369 u64 frame_tick = 0;
370 typename SlotVector<Image>::Iterator deletion_iterator;
356}; 371};
357 372
358template <class P> 373template <class P>
@@ -373,11 +388,94 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
373 // This way the null resource becomes a compile time constant 388 // This way the null resource becomes a compile time constant
374 void(slot_image_views.insert(runtime, NullImageParams{})); 389 void(slot_image_views.insert(runtime, NullImageParams{}));
375 void(slot_samplers.insert(runtime, sampler_descriptor)); 390 void(slot_samplers.insert(runtime, sampler_descriptor));
391
392 deletion_iterator = slot_images.begin();
393
394 if constexpr (HAS_DEVICE_MEMORY_INFO) {
395 const auto device_memory = runtime.GetDeviceLocalMemory();
396 const u64 possible_expected_memory = (device_memory * 3) / 10;
397 const u64 possible_critical_memory = (device_memory * 6) / 10;
398 expected_memory = std::max(possible_expected_memory, DEFAULT_EXPECTED_MEMORY);
399 critical_memory = std::max(possible_critical_memory, DEFAULT_CRITICAL_MEMORY);
400 minimum_memory = 0;
401 } else {
402 // on OGL we can be more conservatives as the driver takes care.
403 expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
404 critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
405 minimum_memory = expected_memory;
406 }
407}
408
409template <class P>
410void TextureCache<P>::RunGarbageCollector() {
411 const bool high_priority_mode = total_used_memory >= expected_memory;
412 const bool aggressive_mode = total_used_memory >= critical_memory;
413 const u64 ticks_to_destroy = high_priority_mode ? 60 : 100;
414 int num_iterations = aggressive_mode ? 256 : (high_priority_mode ? 128 : 64);
415 for (; num_iterations > 0; --num_iterations) {
416 if (deletion_iterator == slot_images.end()) {
417 deletion_iterator = slot_images.begin();
418 if (deletion_iterator == slot_images.end()) {
419 break;
420 }
421 }
422 auto [image_id, image_tmp] = *deletion_iterator;
423 Image* image = image_tmp; // fix clang error.
424 const bool is_alias = True(image->flags & ImageFlagBits::Alias);
425 const bool is_bad_overlap = True(image->flags & ImageFlagBits::BadOverlap);
426 const bool must_download = image->IsSafeDownload();
427 bool should_care = is_bad_overlap || is_alias || (high_priority_mode && !must_download);
428 const u64 ticks_needed =
429 is_bad_overlap
430 ? ticks_to_destroy >> 4
431 : ((should_care && aggressive_mode) ? ticks_to_destroy >> 1 : ticks_to_destroy);
432 should_care |= aggressive_mode;
433 if (should_care && image->frame_tick + ticks_needed < frame_tick) {
434 if (is_bad_overlap) {
435 const bool overlap_check = std::ranges::all_of(
436 image->overlapping_images, [&, image](const ImageId& overlap_id) {
437 auto& overlap = slot_images[overlap_id];
438 return overlap.frame_tick >= image->frame_tick;
439 });
440 if (!overlap_check) {
441 ++deletion_iterator;
442 continue;
443 }
444 }
445 if (!is_bad_overlap && must_download) {
446 const bool alias_check = std::ranges::none_of(
447 image->aliased_images, [&, image](const AliasedImage& alias) {
448 auto& alias_image = slot_images[alias.id];
449 return (alias_image.frame_tick < image->frame_tick) ||
450 (alias_image.modification_tick < image->modification_tick);
451 });
452
453 if (alias_check) {
454 auto map = runtime.DownloadStagingBuffer(image->unswizzled_size_bytes);
455 const auto copies = FullDownloadCopies(image->info);
456 image->DownloadMemory(map, copies);
457 runtime.Finish();
458 SwizzleImage(gpu_memory, image->gpu_addr, image->info, copies, map.mapped_span);
459 }
460 }
461 if (True(image->flags & ImageFlagBits::Tracked)) {
462 UntrackImage(*image);
463 }
464 UnregisterImage(image_id);
465 DeleteImage(image_id);
466 if (is_bad_overlap) {
467 ++num_iterations;
468 }
469 }
470 ++deletion_iterator;
471 }
376} 472}
377 473
378template <class P> 474template <class P>
379void TextureCache<P>::TickFrame() { 475void TextureCache<P>::TickFrame() {
380 // Tick sentenced resources in this order to ensure they are destroyed in the right order 476 if (Settings::values.use_caches_gc.GetValue() && total_used_memory > minimum_memory) {
477 RunGarbageCollector();
478 }
381 sentenced_images.Tick(); 479 sentenced_images.Tick();
382 sentenced_framebuffers.Tick(); 480 sentenced_framebuffers.Tick();
383 sentenced_image_view.Tick(); 481 sentenced_image_view.Tick();
@@ -568,17 +666,7 @@ template <class P>
568void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { 666void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
569 std::vector<ImageId> images; 667 std::vector<ImageId> images;
570 ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { 668 ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) {
571 // Skip images that were not modified from the GPU 669 if (!image.IsSafeDownload()) {
572 if (False(image.flags & ImageFlagBits::GpuModified)) {
573 return;
574 }
575 // Skip images that .are. modified from the CPU
576 // We don't want to write sensitive data from the guest
577 if (True(image.flags & ImageFlagBits::CpuModified)) {
578 return;
579 }
580 if (image.info.num_samples > 1) {
581 LOG_WARNING(HW_GPU, "MSAA image downloads are not implemented");
582 return; 670 return;
583 } 671 }
584 image.flags &= ~ImageFlagBits::GpuModified; 672 image.flags &= ~ImageFlagBits::GpuModified;
@@ -967,6 +1055,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
967 std::vector<ImageId> overlap_ids; 1055 std::vector<ImageId> overlap_ids;
968 std::vector<ImageId> left_aliased_ids; 1056 std::vector<ImageId> left_aliased_ids;
969 std::vector<ImageId> right_aliased_ids; 1057 std::vector<ImageId> right_aliased_ids;
1058 std::vector<ImageId> bad_overlap_ids;
970 ForEachImageInRegion(cpu_addr, size_bytes, [&](ImageId overlap_id, ImageBase& overlap) { 1059 ForEachImageInRegion(cpu_addr, size_bytes, [&](ImageId overlap_id, ImageBase& overlap) {
971 if (info.type != overlap.info.type) { 1060 if (info.type != overlap.info.type) {
972 return; 1061 return;
@@ -992,9 +1081,14 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
992 const ImageBase new_image_base(new_info, gpu_addr, cpu_addr); 1081 const ImageBase new_image_base(new_info, gpu_addr, cpu_addr);
993 if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) { 1082 if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) {
994 left_aliased_ids.push_back(overlap_id); 1083 left_aliased_ids.push_back(overlap_id);
1084 overlap.flags |= ImageFlagBits::Alias;
995 } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options, 1085 } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options,
996 broken_views, native_bgr)) { 1086 broken_views, native_bgr)) {
997 right_aliased_ids.push_back(overlap_id); 1087 right_aliased_ids.push_back(overlap_id);
1088 overlap.flags |= ImageFlagBits::Alias;
1089 } else {
1090 bad_overlap_ids.push_back(overlap_id);
1091 overlap.flags |= ImageFlagBits::BadOverlap;
998 } 1092 }
999 }); 1093 });
1000 const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr); 1094 const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr);
@@ -1022,10 +1116,18 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1022 for (const ImageId aliased_id : right_aliased_ids) { 1116 for (const ImageId aliased_id : right_aliased_ids) {
1023 ImageBase& aliased = slot_images[aliased_id]; 1117 ImageBase& aliased = slot_images[aliased_id];
1024 AddImageAlias(new_image_base, aliased, new_image_id, aliased_id); 1118 AddImageAlias(new_image_base, aliased, new_image_id, aliased_id);
1119 new_image.flags |= ImageFlagBits::Alias;
1025 } 1120 }
1026 for (const ImageId aliased_id : left_aliased_ids) { 1121 for (const ImageId aliased_id : left_aliased_ids) {
1027 ImageBase& aliased = slot_images[aliased_id]; 1122 ImageBase& aliased = slot_images[aliased_id];
1028 AddImageAlias(aliased, new_image_base, aliased_id, new_image_id); 1123 AddImageAlias(aliased, new_image_base, aliased_id, new_image_id);
1124 new_image.flags |= ImageFlagBits::Alias;
1125 }
1126 for (const ImageId aliased_id : bad_overlap_ids) {
1127 ImageBase& aliased = slot_images[aliased_id];
1128 aliased.overlapping_images.push_back(new_image_id);
1129 new_image.overlapping_images.push_back(aliased_id);
1130 new_image.flags |= ImageFlagBits::BadOverlap;
1029 } 1131 }
1030 RegisterImage(new_image_id); 1132 RegisterImage(new_image_id);
1031 return new_image_id; 1133 return new_image_id;
@@ -1195,6 +1297,13 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
1195 image.flags |= ImageFlagBits::Registered; 1297 image.flags |= ImageFlagBits::Registered;
1196 ForEachPage(image.cpu_addr, image.guest_size_bytes, 1298 ForEachPage(image.cpu_addr, image.guest_size_bytes,
1197 [this, image_id](u64 page) { page_table[page].push_back(image_id); }); 1299 [this, image_id](u64 page) { page_table[page].push_back(image_id); });
1300 u64 tentative_size = std::max(image.guest_size_bytes, image.unswizzled_size_bytes);
1301 if ((IsPixelFormatASTC(image.info.format) &&
1302 True(image.flags & ImageFlagBits::AcceleratedUpload)) ||
1303 True(image.flags & ImageFlagBits::Converted)) {
1304 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
1305 }
1306 total_used_memory += Common::AlignUp(tentative_size, 1024);
1198} 1307}
1199 1308
1200template <class P> 1309template <class P>
@@ -1203,6 +1312,14 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1203 ASSERT_MSG(True(image.flags & ImageFlagBits::Registered), 1312 ASSERT_MSG(True(image.flags & ImageFlagBits::Registered),
1204 "Trying to unregister an already registered image"); 1313 "Trying to unregister an already registered image");
1205 image.flags &= ~ImageFlagBits::Registered; 1314 image.flags &= ~ImageFlagBits::Registered;
1315 image.flags &= ~ImageFlagBits::BadOverlap;
1316 u64 tentative_size = std::max(image.guest_size_bytes, image.unswizzled_size_bytes);
1317 if ((IsPixelFormatASTC(image.info.format) &&
1318 True(image.flags & ImageFlagBits::AcceleratedUpload)) ||
1319 True(image.flags & ImageFlagBits::Converted)) {
1320 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
1321 }
1322 total_used_memory -= Common::AlignUp(tentative_size, 1024);
1206 ForEachPage(image.cpu_addr, image.guest_size_bytes, [this, image_id](u64 page) { 1323 ForEachPage(image.cpu_addr, image.guest_size_bytes, [this, image_id](u64 page) {
1207 const auto page_it = page_table.find(page); 1324 const auto page_it = page_table.find(page);
1208 if (page_it == page_table.end()) { 1325 if (page_it == page_table.end()) {
@@ -1276,9 +1393,19 @@ void TextureCache<P>::DeleteImage(ImageId image_id) {
1276 std::erase_if(other_image.aliased_images, [image_id](const AliasedImage& other_alias) { 1393 std::erase_if(other_image.aliased_images, [image_id](const AliasedImage& other_alias) {
1277 return other_alias.id == image_id; 1394 return other_alias.id == image_id;
1278 }); 1395 });
1396 other_image.CheckAliasState();
1279 ASSERT_MSG(num_removed_aliases == 1, "Invalid number of removed aliases: {}", 1397 ASSERT_MSG(num_removed_aliases == 1, "Invalid number of removed aliases: {}",
1280 num_removed_aliases); 1398 num_removed_aliases);
1281 } 1399 }
1400 for (const ImageId overlap_id : image.overlapping_images) {
1401 ImageBase& other_image = slot_images[overlap_id];
1402 [[maybe_unused]] const size_t num_removed_overlaps = std::erase_if(
1403 other_image.overlapping_images,
1404 [image_id](const ImageId other_overlap_id) { return other_overlap_id == image_id; });
1405 other_image.CheckBadOverlapState();
1406 ASSERT_MSG(num_removed_overlaps == 1, "Invalid number of removed overlapps: {}",
1407 num_removed_overlaps);
1408 }
1282 for (const ImageViewId image_view_id : image_view_ids) { 1409 for (const ImageViewId image_view_id : image_view_ids) {
1283 sentenced_image_view.Push(std::move(slot_image_views[image_view_id])); 1410 sentenced_image_view.Push(std::move(slot_image_views[image_view_id]));
1284 slot_image_views.erase(image_view_id); 1411 slot_image_views.erase(image_view_id);
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 6835fd747..4efe042b6 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -581,6 +581,8 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
581 581
582 for (s32 layer = 0; layer < info.resources.layers; ++layer) { 582 for (s32 layer = 0; layer < info.resources.layers; ++layer) {
583 const std::span<const u8> src = input.subspan(host_offset); 583 const std::span<const u8> src = input.subspan(host_offset);
584 gpu_memory.ReadBlockUnsafe(gpu_addr + guest_offset, dst.data(), dst.size_bytes());
585
584 SwizzleTexture(dst, src, bytes_per_block, num_tiles.width, num_tiles.height, 586 SwizzleTexture(dst, src, bytes_per_block, num_tiles.width, num_tiles.height,
585 num_tiles.depth, block.height, block.depth); 587 num_tiles.depth, block.height, block.depth);
586 588
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index 9b2177ebd..7b756ba41 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -269,7 +269,7 @@ static void DecodeQuintBlock(InputBitStream& bits, IntegerEncodedVector& result,
269static void DecodeIntegerSequence(IntegerEncodedVector& result, InputBitStream& bits, u32 maxRange, 269static void DecodeIntegerSequence(IntegerEncodedVector& result, InputBitStream& bits, u32 maxRange,
270 u32 nValues) { 270 u32 nValues) {
271 // Determine encoding parameters 271 // Determine encoding parameters
272 IntegerEncodedValue val = EncodingsValues[maxRange]; 272 IntegerEncodedValue val = ASTC_ENCODINGS_VALUES[maxRange];
273 273
274 // Start decoding 274 // Start decoding
275 u32 nValsDecoded = 0; 275 u32 nValsDecoded = 0;
@@ -310,7 +310,7 @@ struct TexelWeightParams {
310 nIdxs *= 2; 310 nIdxs *= 2;
311 } 311 }
312 312
313 return EncodingsValues[m_MaxWeight].GetBitLength(nIdxs); 313 return ASTC_ENCODINGS_VALUES[m_MaxWeight].GetBitLength(nIdxs);
314 } 314 }
315 315
316 u32 GetNumWeightValues() const { 316 u32 GetNumWeightValues() const {
@@ -551,6 +551,8 @@ static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) {
551 } 551 }
552 } 552 }
553} 553}
554
555static constexpr auto REPLICATE_BYTE_TO_16_TABLE = MakeReplicateTable<u32, 8, 16>();
554static constexpr u32 ReplicateByteTo16(std::size_t value) { 556static constexpr u32 ReplicateByteTo16(std::size_t value) {
555 return REPLICATE_BYTE_TO_16_TABLE[value]; 557 return REPLICATE_BYTE_TO_16_TABLE[value];
556} 558}
@@ -753,12 +755,12 @@ static void DecodeColorValues(u32* out, std::span<u8> data, const u32* modes, co
753 // figure out the max value for each of them... 755 // figure out the max value for each of them...
754 u32 range = 256; 756 u32 range = 256;
755 while (--range > 0) { 757 while (--range > 0) {
756 IntegerEncodedValue val = EncodingsValues[range]; 758 IntegerEncodedValue val = ASTC_ENCODINGS_VALUES[range];
757 u32 bitLength = val.GetBitLength(nValues); 759 u32 bitLength = val.GetBitLength(nValues);
758 if (bitLength <= nBitsForColorData) { 760 if (bitLength <= nBitsForColorData) {
759 // Find the smallest possible range that matches the given encoding 761 // Find the smallest possible range that matches the given encoding
760 while (--range > 0) { 762 while (--range > 0) {
761 IntegerEncodedValue newval = EncodingsValues[range]; 763 IntegerEncodedValue newval = ASTC_ENCODINGS_VALUES[range];
762 if (!newval.MatchesEncoding(val)) { 764 if (!newval.MatchesEncoding(val)) {
763 break; 765 break;
764 } 766 }
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
index c1c37dfe7..0229ae122 100644
--- a/src/video_core/textures/astc.h
+++ b/src/video_core/textures/astc.h
@@ -77,7 +77,7 @@ constexpr std::array<IntegerEncodedValue, 256> MakeEncodedValues() {
77 return encodings; 77 return encodings;
78} 78}
79 79
80constexpr std::array<IntegerEncodedValue, 256> EncodingsValues = MakeEncodedValues(); 80constexpr std::array<IntegerEncodedValue, 256> ASTC_ENCODINGS_VALUES = MakeEncodedValues();
81 81
82// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)] 82// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
83// is the same as [(num_bits - 1):0] and repeats all the way down. 83// is the same as [(num_bits - 1):0] and repeats all the way down.
@@ -116,19 +116,10 @@ constexpr auto MakeReplicateTable() {
116 return table; 116 return table;
117} 117}
118 118
119constexpr auto REPLICATE_BYTE_TO_16_TABLE = MakeReplicateTable<u32, 8, 16>();
120constexpr auto REPLICATE_6_BIT_TO_8_TABLE = MakeReplicateTable<u32, 6, 8>(); 119constexpr auto REPLICATE_6_BIT_TO_8_TABLE = MakeReplicateTable<u32, 6, 8>();
121constexpr auto REPLICATE_7_BIT_TO_8_TABLE = MakeReplicateTable<u32, 7, 8>(); 120constexpr auto REPLICATE_7_BIT_TO_8_TABLE = MakeReplicateTable<u32, 7, 8>();
122constexpr auto REPLICATE_8_BIT_TO_8_TABLE = MakeReplicateTable<u32, 8, 8>(); 121constexpr auto REPLICATE_8_BIT_TO_8_TABLE = MakeReplicateTable<u32, 8, 8>();
123 122
124struct AstcBufferData {
125 decltype(EncodingsValues) encoding_values = EncodingsValues;
126 decltype(REPLICATE_6_BIT_TO_8_TABLE) replicate_6_to_8 = REPLICATE_6_BIT_TO_8_TABLE;
127 decltype(REPLICATE_7_BIT_TO_8_TABLE) replicate_7_to_8 = REPLICATE_7_BIT_TO_8_TABLE;
128 decltype(REPLICATE_8_BIT_TO_8_TABLE) replicate_8_to_8 = REPLICATE_8_BIT_TO_8_TABLE;
129 decltype(REPLICATE_BYTE_TO_16_TABLE) replicate_byte_to_16 = REPLICATE_BYTE_TO_16_TABLE;
130} constexpr ASTC_BUFFER_DATA;
131
132void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth, 123void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth,
133 uint32_t block_width, uint32_t block_height, std::span<uint8_t> output); 124 uint32_t block_width, uint32_t block_height, std::span<uint8_t> output);
134 125
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
index f0ee76519..758c038ba 100644
--- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
@@ -50,7 +50,7 @@ NsightAftermathTracker::NsightAftermathTracker() {
50 } 50 }
51 dump_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::LogDir) / "gpucrash"; 51 dump_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::LogDir) / "gpucrash";
52 52
53 void(Common::FS::RemoveDirRecursively(dump_dir)); 53 Common::FS::RemoveDirRecursively(dump_dir);
54 if (!Common::FS::CreateDir(dump_dir)) { 54 if (!Common::FS::CreateDir(dump_dir)) {
55 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory"); 55 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
56 return; 56 return;
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index c52cd246b..f214510da 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -408,6 +408,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
408 } 408 }
409 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld); 409 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
410 410
411 CollectPhysicalMemoryInfo();
411 CollectTelemetryParameters(); 412 CollectTelemetryParameters();
412 CollectToolingInfo(); 413 CollectToolingInfo();
413 414
@@ -839,6 +840,17 @@ void Device::CollectTelemetryParameters() {
839 } 840 }
840} 841}
841 842
843void Device::CollectPhysicalMemoryInfo() {
844 const auto mem_properties = physical.GetMemoryProperties();
845 const size_t num_properties = mem_properties.memoryHeapCount;
846 device_access_memory = 0;
847 for (size_t element = 0; element < num_properties; ++element) {
848 if ((mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) {
849 device_access_memory += mem_properties.memoryHeaps[element].size;
850 }
851 }
852}
853
842void Device::CollectToolingInfo() { 854void Device::CollectToolingInfo() {
843 if (!ext_tooling_info) { 855 if (!ext_tooling_info) {
844 return; 856 return;
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index c5504467d..96c0f8c60 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -228,6 +228,10 @@ public:
228 return use_asynchronous_shaders; 228 return use_asynchronous_shaders;
229 } 229 }
230 230
231 u64 GetDeviceLocalMemory() const {
232 return device_access_memory;
233 }
234
231private: 235private:
232 /// Checks if the physical device is suitable. 236 /// Checks if the physical device is suitable.
233 void CheckSuitability(bool requires_swapchain) const; 237 void CheckSuitability(bool requires_swapchain) const;
@@ -247,6 +251,9 @@ private:
247 /// Collects information about attached tools. 251 /// Collects information about attached tools.
248 void CollectToolingInfo(); 252 void CollectToolingInfo();
249 253
254 /// Collects information about the device's local memory.
255 void CollectPhysicalMemoryInfo();
256
250 /// Returns a list of queue initialization descriptors. 257 /// Returns a list of queue initialization descriptors.
251 std::vector<VkDeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const; 258 std::vector<VkDeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
252 259
@@ -260,21 +267,22 @@ private:
260 bool IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, 267 bool IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
261 FormatType format_type) const; 268 FormatType format_type) const;
262 269
263 VkInstance instance; ///< Vulkan instance. 270 VkInstance instance; ///< Vulkan instance.
264 vk::DeviceDispatch dld; ///< Device function pointers. 271 vk::DeviceDispatch dld; ///< Device function pointers.
265 vk::PhysicalDevice physical; ///< Physical device. 272 vk::PhysicalDevice physical; ///< Physical device.
266 VkPhysicalDeviceProperties properties; ///< Device properties. 273 VkPhysicalDeviceProperties properties; ///< Device properties.
267 vk::Device logical; ///< Logical device. 274 vk::Device logical; ///< Logical device.
268 vk::Queue graphics_queue; ///< Main graphics queue. 275 vk::Queue graphics_queue; ///< Main graphics queue.
269 vk::Queue present_queue; ///< Main present queue. 276 vk::Queue present_queue; ///< Main present queue.
270 u32 instance_version{}; ///< Vulkan onstance version. 277 u32 instance_version{}; ///< Vulkan onstance version.
271 u32 graphics_family{}; ///< Main graphics queue family index. 278 u32 graphics_family{}; ///< Main graphics queue family index.
272 u32 present_family{}; ///< Main present queue family index. 279 u32 present_family{}; ///< Main present queue family index.
273 VkDriverIdKHR driver_id{}; ///< Driver ID. 280 VkDriverIdKHR driver_id{}; ///< Driver ID.
274 VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced.ed 281 VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced.
275 bool is_optimal_astc_supported{}; ///< Support for native ASTC. 282 u64 device_access_memory{}; ///< Total size of device local memory in bytes.
276 bool is_float16_supported{}; ///< Support for float16 arithmetics. 283 bool is_optimal_astc_supported{}; ///< Support for native ASTC.
277 bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. 284 bool is_float16_supported{}; ///< Support for float16 arithmetics.
285 bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
278 bool is_formatless_image_load_supported{}; ///< Support for shader image read without format. 286 bool is_formatless_image_load_supported{}; ///< Support for shader image read without format.
279 bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images. 287 bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images.
280 bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil. 288 bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index 5edd06ebc..aa173d19e 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -69,10 +69,10 @@ constexpr VkExportMemoryAllocateInfo EXPORT_ALLOCATE_INFO{
69 69
70class MemoryAllocation { 70class MemoryAllocation {
71public: 71public:
72 explicit MemoryAllocation(vk::DeviceMemory memory_, VkMemoryPropertyFlags properties, 72 explicit MemoryAllocation(MemoryAllocator* const allocator_, vk::DeviceMemory memory_,
73 u64 allocation_size_, u32 type) 73 VkMemoryPropertyFlags properties, u64 allocation_size_, u32 type)
74 : memory{std::move(memory_)}, allocation_size{allocation_size_}, property_flags{properties}, 74 : allocator{allocator_}, memory{std::move(memory_)}, allocation_size{allocation_size_},
75 shifted_memory_type{1U << type} {} 75 property_flags{properties}, shifted_memory_type{1U << type} {}
76 76
77#if defined(_WIN32) || defined(__unix__) 77#if defined(_WIN32) || defined(__unix__)
78 ~MemoryAllocation() { 78 ~MemoryAllocation() {
@@ -106,6 +106,10 @@ public:
106 const auto it = std::ranges::find(commits, begin, &Range::begin); 106 const auto it = std::ranges::find(commits, begin, &Range::begin);
107 ASSERT_MSG(it != commits.end(), "Invalid commit"); 107 ASSERT_MSG(it != commits.end(), "Invalid commit");
108 commits.erase(it); 108 commits.erase(it);
109 if (commits.empty()) {
110 // Do not call any code involving 'this' after this call, the object will be destroyed
111 allocator->ReleaseMemory(this);
112 }
109 } 113 }
110 114
111 [[nodiscard]] std::span<u8> Map() { 115 [[nodiscard]] std::span<u8> Map() {
@@ -171,6 +175,7 @@ private:
171 return candidate; 175 return candidate;
172 } 176 }
173 177
178 MemoryAllocator* const allocator; ///< Parent memory allocation.
174 const vk::DeviceMemory memory; ///< Vulkan memory allocation handler. 179 const vk::DeviceMemory memory; ///< Vulkan memory allocation handler.
175 const u64 allocation_size; ///< Size of this allocation. 180 const u64 allocation_size; ///< Size of this allocation.
176 const VkMemoryPropertyFlags property_flags; ///< Vulkan memory property flags. 181 const VkMemoryPropertyFlags property_flags; ///< Vulkan memory property flags.
@@ -275,10 +280,17 @@ bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask,
275 return false; 280 return false;
276 } 281 }
277 } 282 }
278 allocations.push_back(std::make_unique<MemoryAllocation>(std::move(memory), flags, size, type)); 283 allocations.push_back(
284 std::make_unique<MemoryAllocation>(this, std::move(memory), flags, size, type));
279 return true; 285 return true;
280} 286}
281 287
288void MemoryAllocator::ReleaseMemory(MemoryAllocation* alloc) {
289 const auto it = std::ranges::find(allocations, alloc, &std::unique_ptr<MemoryAllocation>::get);
290 ASSERT(it != allocations.end());
291 allocations.erase(it);
292}
293
282std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, 294std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements,
283 VkMemoryPropertyFlags flags) { 295 VkMemoryPropertyFlags flags) {
284 for (auto& allocation : allocations) { 296 for (auto& allocation : allocations) {
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h
index db12d02f4..b61e931e0 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.h
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h
@@ -69,6 +69,8 @@ private:
69/// Memory allocator container. 69/// Memory allocator container.
70/// Allocates and releases memory allocations on demand. 70/// Allocates and releases memory allocations on demand.
71class MemoryAllocator { 71class MemoryAllocator {
72 friend MemoryAllocation;
73
72public: 74public:
73 /** 75 /**
74 * Construct memory allocator 76 * Construct memory allocator
@@ -104,6 +106,9 @@ private:
104 /// Tries to allocate a chunk of memory. 106 /// Tries to allocate a chunk of memory.
105 bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); 107 bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size);
106 108
109 /// Releases a chunk of memory.
110 void ReleaseMemory(MemoryAllocation* alloc);
111
107 /// Tries to allocate a memory commit. 112 /// Tries to allocate a memory commit.
108 std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements, 113 std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements,
109 VkMemoryPropertyFlags flags); 114 VkMemoryPropertyFlags flags);
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 67183e64c..e04f7dfc6 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -100,8 +100,9 @@ struct Client::Impl {
100 request.body = data; 100 request.body = data;
101 101
102 httplib::Response response; 102 httplib::Response response;
103 httplib::Error error;
103 104
104 if (!cli->send(request, response)) { 105 if (!cli->send(request, response, error)) {
105 LOG_ERROR(WebService, "{} to {} returned null", method, host + path); 106 LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
106 return WebResult{WebResult::Code::LibError, "Null response", ""}; 107 return WebResult{WebResult::Code::LibError, "Null response", ""};
107 } 108 }
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp
index a2e0e6962..6b0155a78 100644
--- a/src/yuzu/about_dialog.cpp
+++ b/src/yuzu/about_dialog.cpp
@@ -14,7 +14,8 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
14 const auto build_id = std::string(Common::g_build_id); 14 const auto build_id = std::string(Common::g_build_id);
15 15
16 const auto yuzu_build = fmt::format("yuzu Development Build | {}-{}", branch_name, description); 16 const auto yuzu_build = fmt::format("yuzu Development Build | {}-{}", branch_name, description);
17 const auto override_build = fmt::format(std::string(Common::g_title_bar_format_idle), build_id); 17 const auto override_build =
18 fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
18 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; 19 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
19 20
20 ui->setupUi(this); 21 ui->setupUi(this);
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 86495803e..7524e3c40 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -51,11 +51,11 @@ void EmuThread::run() {
51 Common::SetCurrentThreadName(name.c_str()); 51 Common::SetCurrentThreadName(name.c_str());
52 52
53 auto& system = Core::System::GetInstance(); 53 auto& system = Core::System::GetInstance();
54 auto& gpu = system.GPU();
55 auto stop_token = stop_source.get_token();
54 56
55 system.RegisterHostThread(); 57 system.RegisterHostThread();
56 58
57 auto& gpu = system.GPU();
58
59 // Main process has been loaded. Make the context current to this thread and begin GPU and CPU 59 // Main process has been loaded. Make the context current to this thread and begin GPU and CPU
60 // execution. 60 // execution.
61 gpu.Start(); 61 gpu.Start();
@@ -65,7 +65,7 @@ void EmuThread::run() {
65 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 65 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
66 66
67 system.Renderer().ReadRasterizer()->LoadDiskResources( 67 system.Renderer().ReadRasterizer()->LoadDiskResources(
68 system.CurrentProcess()->GetTitleID(), stop_run, 68 system.CurrentProcess()->GetTitleID(), stop_token,
69 [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { 69 [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
70 emit LoadProgress(stage, value, total); 70 emit LoadProgress(stage, value, total);
71 }); 71 });
@@ -78,7 +78,7 @@ void EmuThread::run() {
78 // so that the DebugModeLeft signal can be emitted before the 78 // so that the DebugModeLeft signal can be emitted before the
79 // next execution step 79 // next execution step
80 bool was_active = false; 80 bool was_active = false;
81 while (!stop_run) { 81 while (!stop_token.stop_requested()) {
82 if (running) { 82 if (running) {
83 if (was_active) { 83 if (was_active) {
84 emit DebugModeLeft(); 84 emit DebugModeLeft();
@@ -100,7 +100,7 @@ void EmuThread::run() {
100 } 100 }
101 running_guard = false; 101 running_guard = false;
102 102
103 if (!stop_run) { 103 if (!stop_token.stop_requested()) {
104 was_active = true; 104 was_active = true;
105 emit DebugModeEntered(); 105 emit DebugModeEntered();
106 } 106 }
@@ -108,7 +108,7 @@ void EmuThread::run() {
108 UNIMPLEMENTED(); 108 UNIMPLEMENTED();
109 } else { 109 } else {
110 std::unique_lock lock{running_mutex}; 110 std::unique_lock lock{running_mutex};
111 running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); 111 running_cv.wait(lock, stop_token, [this] { return IsRunning() || exec_step; });
112 } 112 }
113 } 113 }
114 114
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index acfe2bc8c..402dd2ee1 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -89,16 +89,16 @@ public:
89 * Requests for the emulation thread to stop running 89 * Requests for the emulation thread to stop running
90 */ 90 */
91 void RequestStop() { 91 void RequestStop() {
92 stop_run = true; 92 stop_source.request_stop();
93 SetRunning(false); 93 SetRunning(false);
94 } 94 }
95 95
96private: 96private:
97 bool exec_step = false; 97 bool exec_step = false;
98 bool running = false; 98 bool running = false;
99 std::atomic_bool stop_run{false}; 99 std::stop_source stop_source;
100 std::mutex running_mutex; 100 std::mutex running_mutex;
101 std::condition_variable running_cv; 101 std::condition_variable_any running_cv;
102 Common::Event running_wait{}; 102 Common::Event running_wait{};
103 std::atomic_bool running_guard{false}; 103 std::atomic_bool running_guard{false};
104 104
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 916a22724..62bafc453 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -221,7 +221,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
221// This must be in alphabetical order according to action name as it must have the same order as 221// This must be in alphabetical order according to action name as it must have the same order as
222// UISetting::values.shortcuts, which is alphabetically ordered. 222// UISetting::values.shortcuts, which is alphabetically ordered.
223// clang-format off 223// clang-format off
224const std::array<UISettings::Shortcut, 17> Config::default_hotkeys{{ 224const std::array<UISettings::Shortcut, 18> Config::default_hotkeys{{
225 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}}, 225 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
226 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, 226 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
227 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, 227 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
@@ -236,6 +236,7 @@ const std::array<UISettings::Shortcut, 17> Config::default_hotkeys{{
236 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, 236 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
237 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, 237 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
238 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, 238 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
239 {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), Qt::ApplicationShortcut}},
239 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}}, 240 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},
240 {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, 241 {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
241 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, 242 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
@@ -756,6 +757,8 @@ void Config::ReadCpuValues() {
756 QStringLiteral("cpuopt_unsafe_unfuse_fma"), true); 757 QStringLiteral("cpuopt_unsafe_unfuse_fma"), true);
757 ReadSettingGlobal(Settings::values.cpuopt_unsafe_reduce_fp_error, 758 ReadSettingGlobal(Settings::values.cpuopt_unsafe_reduce_fp_error,
758 QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true); 759 QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true);
760 ReadSettingGlobal(Settings::values.cpuopt_unsafe_ignore_standard_fpcr,
761 QStringLiteral("cpuopt_unsafe_ignore_standard_fpcr"), true);
759 ReadSettingGlobal(Settings::values.cpuopt_unsafe_inaccurate_nan, 762 ReadSettingGlobal(Settings::values.cpuopt_unsafe_inaccurate_nan,
760 QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true); 763 QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true);
761 ReadSettingGlobal(Settings::values.cpuopt_unsafe_fastmem_check, 764 ReadSettingGlobal(Settings::values.cpuopt_unsafe_fastmem_check,
@@ -811,12 +814,15 @@ void Config::ReadRendererValues() {
811 true); 814 true);
812 ReadSettingGlobal(Settings::values.accelerate_astc, QStringLiteral("accelerate_astc"), true); 815 ReadSettingGlobal(Settings::values.accelerate_astc, QStringLiteral("accelerate_astc"), true);
813 ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true); 816 ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
817 ReadSettingGlobal(Settings::values.disable_fps_limit, QStringLiteral("disable_fps_limit"),
818 false);
814 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"), 819 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
815 false); 820 false);
816 ReadSettingGlobal(Settings::values.use_asynchronous_shaders, 821 ReadSettingGlobal(Settings::values.use_asynchronous_shaders,
817 QStringLiteral("use_asynchronous_shaders"), false); 822 QStringLiteral("use_asynchronous_shaders"), false);
818 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), 823 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
819 true); 824 true);
825 ReadSettingGlobal(Settings::values.use_caches_gc, QStringLiteral("use_caches_gc"), false);
820 ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0); 826 ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
821 ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0); 827 ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
822 ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0); 828 ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
@@ -1339,6 +1345,8 @@ void Config::SaveCpuValues() {
1339 Settings::values.cpuopt_unsafe_unfuse_fma, true); 1345 Settings::values.cpuopt_unsafe_unfuse_fma, true);
1340 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), 1346 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),
1341 Settings::values.cpuopt_unsafe_reduce_fp_error, true); 1347 Settings::values.cpuopt_unsafe_reduce_fp_error, true);
1348 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_ignore_standard_fpcr"),
1349 Settings::values.cpuopt_unsafe_ignore_standard_fpcr, true);
1342 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), 1350 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_inaccurate_nan"),
1343 Settings::values.cpuopt_unsafe_inaccurate_nan, true); 1351 Settings::values.cpuopt_unsafe_inaccurate_nan, true);
1344 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_fastmem_check"), 1352 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_fastmem_check"),
@@ -1395,12 +1403,15 @@ void Config::SaveRendererValues() {
1395 true); 1403 true);
1396 WriteSettingGlobal(QStringLiteral("accelerate_astc"), Settings::values.accelerate_astc, true); 1404 WriteSettingGlobal(QStringLiteral("accelerate_astc"), Settings::values.accelerate_astc, true);
1397 WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); 1405 WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
1406 WriteSettingGlobal(QStringLiteral("disable_fps_limit"), Settings::values.disable_fps_limit,
1407 false);
1398 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"), 1408 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
1399 Settings::values.use_assembly_shaders, false); 1409 Settings::values.use_assembly_shaders, false);
1400 WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"), 1410 WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"),
1401 Settings::values.use_asynchronous_shaders, false); 1411 Settings::values.use_asynchronous_shaders, false);
1402 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, 1412 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
1403 true); 1413 true);
1414 WriteSettingGlobal(QStringLiteral("use_caches_gc"), Settings::values.use_caches_gc, false);
1404 // Cast to double because Qt's written float values are not human-readable 1415 // Cast to double because Qt's written float values are not human-readable
1405 WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0); 1416 WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
1406 WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0); 1417 WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index ce3355588..3c1de0ac9 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -42,7 +42,7 @@ public:
42 default_mouse_buttons; 42 default_mouse_buttons;
43 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; 43 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
44 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; 44 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
45 static const std::array<UISettings::Shortcut, 17> default_hotkeys; 45 static const std::array<UISettings::Shortcut, 18> default_hotkeys;
46 46
47private: 47private:
48 void Initialize(const std::string& config_name); 48 void Initialize(const std::string& config_name);
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 22219cbad..13db2ba98 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -34,12 +34,15 @@ void ConfigureCpu::SetConfiguration() {
34 ui->accuracy->setEnabled(runtime_lock); 34 ui->accuracy->setEnabled(runtime_lock);
35 ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock); 35 ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock);
36 ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); 36 ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock);
37 ui->cpuopt_unsafe_ignore_standard_fpcr->setEnabled(runtime_lock);
37 ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); 38 ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock);
38 ui->cpuopt_unsafe_fastmem_check->setEnabled(runtime_lock); 39 ui->cpuopt_unsafe_fastmem_check->setEnabled(runtime_lock);
39 40
40 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()); 41 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue());
41 ui->cpuopt_unsafe_reduce_fp_error->setChecked( 42 ui->cpuopt_unsafe_reduce_fp_error->setChecked(
42 Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()); 43 Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue());
44 ui->cpuopt_unsafe_ignore_standard_fpcr->setChecked(
45 Settings::values.cpuopt_unsafe_ignore_standard_fpcr.GetValue());
43 ui->cpuopt_unsafe_inaccurate_nan->setChecked( 46 ui->cpuopt_unsafe_inaccurate_nan->setChecked(
44 Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()); 47 Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue());
45 ui->cpuopt_unsafe_fastmem_check->setChecked( 48 ui->cpuopt_unsafe_fastmem_check->setChecked(
@@ -84,6 +87,9 @@ void ConfigureCpu::ApplyConfiguration() {
84 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_reduce_fp_error, 87 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_reduce_fp_error,
85 ui->cpuopt_unsafe_reduce_fp_error, 88 ui->cpuopt_unsafe_reduce_fp_error,
86 cpuopt_unsafe_reduce_fp_error); 89 cpuopt_unsafe_reduce_fp_error);
90 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_standard_fpcr,
91 ui->cpuopt_unsafe_ignore_standard_fpcr,
92 cpuopt_unsafe_ignore_standard_fpcr);
87 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_inaccurate_nan, 93 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_inaccurate_nan,
88 ui->cpuopt_unsafe_inaccurate_nan, 94 ui->cpuopt_unsafe_inaccurate_nan,
89 cpuopt_unsafe_inaccurate_nan); 95 cpuopt_unsafe_inaccurate_nan);
@@ -137,6 +143,9 @@ void ConfigureCpu::SetupPerGameUI() {
137 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_reduce_fp_error, 143 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_reduce_fp_error,
138 Settings::values.cpuopt_unsafe_reduce_fp_error, 144 Settings::values.cpuopt_unsafe_reduce_fp_error,
139 cpuopt_unsafe_reduce_fp_error); 145 cpuopt_unsafe_reduce_fp_error);
146 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_standard_fpcr,
147 Settings::values.cpuopt_unsafe_ignore_standard_fpcr,
148 cpuopt_unsafe_ignore_standard_fpcr);
140 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_inaccurate_nan, 149 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_inaccurate_nan,
141 Settings::values.cpuopt_unsafe_inaccurate_nan, 150 Settings::values.cpuopt_unsafe_inaccurate_nan,
142 cpuopt_unsafe_inaccurate_nan); 151 cpuopt_unsafe_inaccurate_nan);
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
index 57ff2772a..b2b5f1671 100644
--- a/src/yuzu/configuration/configure_cpu.h
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -40,6 +40,7 @@ private:
40 40
41 ConfigurationShared::CheckState cpuopt_unsafe_unfuse_fma; 41 ConfigurationShared::CheckState cpuopt_unsafe_unfuse_fma;
42 ConfigurationShared::CheckState cpuopt_unsafe_reduce_fp_error; 42 ConfigurationShared::CheckState cpuopt_unsafe_reduce_fp_error;
43 ConfigurationShared::CheckState cpuopt_unsafe_ignore_standard_fpcr;
43 ConfigurationShared::CheckState cpuopt_unsafe_inaccurate_nan; 44 ConfigurationShared::CheckState cpuopt_unsafe_inaccurate_nan;
44 ConfigurationShared::CheckState cpuopt_unsafe_fastmem_check; 45 ConfigurationShared::CheckState cpuopt_unsafe_fastmem_check;
45}; 46};
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index 31ef9e3f5..0e296d4e5 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -112,6 +112,18 @@
112 </widget> 112 </widget>
113 </item> 113 </item>
114 <item> 114 <item>
115 <widget class="QCheckBox" name="cpuopt_unsafe_ignore_standard_fpcr">
116 <property name="toolTip">
117 <string>
118 &lt;div&gt;This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes.&lt;/div&gt;
119 </string>
120 </property>
121 <property name="text">
122 <string>Faster ASIMD instructions (32 bits only)</string>
123 </property>
124 </widget>
125 </item>
126 <item>
115 <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan"> 127 <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan">
116 <property name="toolTip"> 128 <property name="toolTip">
117 <string> 129 <string>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 35bf9c6be..8d13c9857 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -28,8 +28,10 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
29 29
30 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); 30 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
31 ui->disable_fps_limit->setChecked(Settings::values.disable_fps_limit.GetValue());
31 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); 32 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
32 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 33 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
34 ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue());
33 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 35 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
34 36
35 if (Settings::IsConfiguringGlobal()) { 37 if (Settings::IsConfiguringGlobal()) {
@@ -57,11 +59,15 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
57 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, 59 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
58 ui->anisotropic_filtering_combobox); 60 ui->anisotropic_filtering_combobox);
59 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); 61 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync);
62 ConfigurationShared::ApplyPerGameSetting(&Settings::values.disable_fps_limit,
63 ui->disable_fps_limit, disable_fps_limit);
60 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, 64 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
61 ui->use_assembly_shaders, use_assembly_shaders); 65 ui->use_assembly_shaders, use_assembly_shaders);
62 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, 66 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
63 ui->use_asynchronous_shaders, 67 ui->use_asynchronous_shaders,
64 use_asynchronous_shaders); 68 use_asynchronous_shaders);
69 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_caches_gc, ui->use_caches_gc,
70 use_caches_gc);
65 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, 71 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
66 ui->use_fast_gpu_time, use_fast_gpu_time); 72 ui->use_fast_gpu_time, use_fast_gpu_time);
67 73
@@ -97,10 +103,12 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
97 if (Settings::IsConfiguringGlobal()) { 103 if (Settings::IsConfiguringGlobal()) {
98 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); 104 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
99 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); 105 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
106 ui->disable_fps_limit->setEnabled(Settings::values.disable_fps_limit.UsingGlobal());
100 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); 107 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
101 ui->use_asynchronous_shaders->setEnabled( 108 ui->use_asynchronous_shaders->setEnabled(
102 Settings::values.use_asynchronous_shaders.UsingGlobal()); 109 Settings::values.use_asynchronous_shaders.UsingGlobal());
103 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); 110 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
111 ui->use_caches_gc->setEnabled(Settings::values.use_caches_gc.UsingGlobal());
104 ui->anisotropic_filtering_combobox->setEnabled( 112 ui->anisotropic_filtering_combobox->setEnabled(
105 Settings::values.max_anisotropy.UsingGlobal()); 113 Settings::values.max_anisotropy.UsingGlobal());
106 114
@@ -108,6 +116,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
108 } 116 }
109 117
110 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); 118 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync);
119 ConfigurationShared::SetColoredTristate(ui->disable_fps_limit,
120 Settings::values.disable_fps_limit, disable_fps_limit);
111 ConfigurationShared::SetColoredTristate( 121 ConfigurationShared::SetColoredTristate(
112 ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders); 122 ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders);
113 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, 123 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
@@ -115,6 +125,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
115 use_asynchronous_shaders); 125 use_asynchronous_shaders);
116 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, 126 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
117 Settings::values.use_fast_gpu_time, use_fast_gpu_time); 127 Settings::values.use_fast_gpu_time, use_fast_gpu_time);
128 ConfigurationShared::SetColoredTristate(ui->use_caches_gc, Settings::values.use_caches_gc,
129 use_caches_gc);
118 ConfigurationShared::SetColoredComboBox( 130 ConfigurationShared::SetColoredComboBox(
119 ui->gpu_accuracy, ui->label_gpu_accuracy, 131 ui->gpu_accuracy, ui->label_gpu_accuracy,
120 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); 132 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index e61b571c7..6ac5f20ec 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -35,7 +35,9 @@ private:
35 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 35 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
36 36
37 ConfigurationShared::CheckState use_vsync; 37 ConfigurationShared::CheckState use_vsync;
38 ConfigurationShared::CheckState disable_fps_limit;
38 ConfigurationShared::CheckState use_assembly_shaders; 39 ConfigurationShared::CheckState use_assembly_shaders;
39 ConfigurationShared::CheckState use_asynchronous_shaders; 40 ConfigurationShared::CheckState use_asynchronous_shaders;
40 ConfigurationShared::CheckState use_fast_gpu_time; 41 ConfigurationShared::CheckState use_fast_gpu_time;
42 ConfigurationShared::CheckState use_caches_gc;
41}; 43};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 846a30586..18c43629e 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -77,6 +77,24 @@
77 </widget> 77 </widget>
78 </item> 78 </item>
79 <item> 79 <item>
80 <widget class="QCheckBox" name="disable_fps_limit">
81 <property name="enabled">
82 <bool>true</bool>
83 </property>
84 <property name="toolTip">
85 <string>
86 &lt;html&gt;&lt;head/&gt;&lt;body&gt;
87 &lt;p&gt;Presents guest frames as they become available, disabling the FPS limit in most titles.&lt;/p&gt;
88 &lt;p&gt;NOTE: Will cause instabilities.&lt;/p&gt;
89 &lt;/body&gt;&lt;/html&gt;
90 </string>
91 </property>
92 <property name="text">
93 <string>Disable framerate limit (experimental)</string>
94 </property>
95 </widget>
96 </item>
97 <item>
80 <widget class="QCheckBox" name="use_assembly_shaders"> 98 <widget class="QCheckBox" name="use_assembly_shaders">
81 <property name="toolTip"> 99 <property name="toolTip">
82 <string>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</string> 100 <string>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</string>
@@ -104,6 +122,16 @@
104 </widget> 122 </widget>
105 </item> 123 </item>
106 <item> 124 <item>
125 <widget class="QCheckBox" name="use_caches_gc">
126 <property name="toolTip">
127 <string>Enables garbage collection for the GPU caches, this will try to keep VRAM within 3-4 GB by flushing the least used textures/buffers. May cause issues in a few games.</string>
128 </property>
129 <property name="text">
130 <string>Enable GPU cache garbage collection (experimental)</string>
131 </property>
132 </widget>
133 </item>
134 <item>
107 <widget class="QWidget" name="af_layout" native="true"> 135 <widget class="QWidget" name="af_layout" native="true">
108 <layout class="QHBoxLayout" name="horizontalLayout_1"> 136 <layout class="QHBoxLayout" name="horizontalLayout_1">
109 <property name="leftMargin"> 137 <property name="leftMargin">
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index a1d434aca..8c00eec59 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -47,6 +47,8 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id, const std::str
47 ui->setupUi(this); 47 ui->setupUi(this);
48 setFocusPolicy(Qt::ClickFocus); 48 setFocusPolicy(Qt::ClickFocus);
49 setWindowTitle(tr("Properties")); 49 setWindowTitle(tr("Properties"));
50 // remove Help question mark button from the title bar
51 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
50 52
51 ui->addonsTab->SetTitleId(title_id); 53 ui->addonsTab->SetTitleId(title_id);
52 54
diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui
index adf6d0b39..7da14146b 100644
--- a/src/yuzu/configuration/configure_per_game.ui
+++ b/src/yuzu/configuration/configure_per_game.ui
@@ -6,10 +6,15 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>800</width> 9 <width>900</width>
10 <height>600</height> 10 <height>600</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="minimumSize">
14 <size>
15 <width>900</width>
16 </size>
17 </property>
13 <property name="windowTitle"> 18 <property name="windowTitle">
14 <string>Dialog</string> 19 <string>Dialog</string>
15 </property> 20 </property>
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 9b709d405..ebb0f411c 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -79,8 +79,8 @@ void ConfigurePerGameAddons::ApplyConfiguration() {
79 std::sort(disabled_addons.begin(), disabled_addons.end()); 79 std::sort(disabled_addons.begin(), disabled_addons.end());
80 std::sort(current.begin(), current.end()); 80 std::sort(current.begin(), current.end());
81 if (disabled_addons != current) { 81 if (disabled_addons != current) {
82 void(Common::FS::RemoveFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / 82 Common::FS::RemoveFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
83 "game_list" / fmt::format("{:016X}.pv.txt", title_id))); 83 "game_list" / fmt::format("{:016X}.pv.txt", title_id));
84 } 84 }
85 85
86 Settings::values.disabled_addons[title_id] = disabled_addons; 86 Settings::values.disabled_addons[title_id] = disabled_addons;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f4dd53b9b..7063327e8 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -195,10 +195,10 @@ static void RemoveCachedContents() {
195 const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information"; 195 const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information";
196 const auto offline_system_data = cache_dir / "offline_web_applet_system_data"; 196 const auto offline_system_data = cache_dir / "offline_web_applet_system_data";
197 197
198 void(Common::FS::RemoveDirRecursively(offline_fonts)); 198 Common::FS::RemoveDirRecursively(offline_fonts);
199 void(Common::FS::RemoveDirRecursively(offline_manual)); 199 Common::FS::RemoveDirRecursively(offline_manual);
200 void(Common::FS::RemoveDirRecursively(offline_legal_information)); 200 Common::FS::RemoveDirRecursively(offline_legal_information);
201 void(Common::FS::RemoveDirRecursively(offline_system_data)); 201 Common::FS::RemoveDirRecursively(offline_system_data);
202} 202}
203 203
204GMainWindow::GMainWindow() 204GMainWindow::GMainWindow()
@@ -237,7 +237,8 @@ GMainWindow::GMainWindow()
237 const auto build_id = std::string(Common::g_build_id); 237 const auto build_id = std::string(Common::g_build_id);
238 238
239 const auto yuzu_build = fmt::format("yuzu Development Build | {}-{}", branch_name, description); 239 const auto yuzu_build = fmt::format("yuzu Development Build | {}-{}", branch_name, description);
240 const auto override_build = fmt::format(std::string(Common::g_title_bar_format_idle), build_id); 240 const auto override_build =
241 fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
241 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; 242 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
242 243
243 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); 244 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);
@@ -1026,7 +1027,11 @@ void GMainWindow::InitializeHotkeys() {
1026 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this), 1027 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this),
1027 &QShortcut::activated, this, 1028 &QShortcut::activated, this,
1028 [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); 1029 [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
1029 1030 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Framerate Limit"), this),
1031 &QShortcut::activated, this, [] {
1032 Settings::values.disable_fps_limit.SetValue(
1033 !Settings::values.disable_fps_limit.GetValue());
1034 });
1030 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this), 1035 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this),
1031 &QShortcut::activated, this, [&] { 1036 &QShortcut::activated, this, [&] {
1032 Settings::values.mouse_panning = !Settings::values.mouse_panning; 1037 Settings::values.mouse_panning = !Settings::values.mouse_panning;
@@ -1418,6 +1423,9 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
1418 title_name = Common::FS::PathToUTF8String( 1423 title_name = Common::FS::PathToUTF8String(
1419 std::filesystem::path{filename.toStdU16String()}.filename()); 1424 std::filesystem::path{filename.toStdU16String()}.filename());
1420 } 1425 }
1426 const bool is_64bit = system.Kernel().CurrentProcess()->Is64BitProcess();
1427 const auto instruction_set_suffix = is_64bit ? " (64-bit)" : " (32-bit)";
1428 title_name += instruction_set_suffix;
1421 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); 1429 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
1422 const auto gpu_vendor = system.GPU().Renderer().GetDeviceVendor(); 1430 const auto gpu_vendor = system.GPU().Renderer().GetDeviceVendor();
1423 UpdateWindowTitle(title_name, title_version, gpu_vendor); 1431 UpdateWindowTitle(title_name, title_version, gpu_vendor);
@@ -1741,8 +1749,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
1741 RemoveAddOnContent(program_id, entry_type); 1749 RemoveAddOnContent(program_id, entry_type);
1742 break; 1750 break;
1743 } 1751 }
1744 void(Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / 1752 Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
1745 "game_list")); 1753 "game_list");
1746 game_list->PopulateAsync(UISettings::values.game_dirs); 1754 game_list->PopulateAsync(UISettings::values.game_dirs);
1747} 1755}
1748 1756
@@ -2211,8 +2219,8 @@ void GMainWindow::OnMenuInstallToNAND() {
2211 : tr("%n file(s) failed to install\n", "", failed_files.size())); 2219 : tr("%n file(s) failed to install\n", "", failed_files.size()));
2212 2220
2213 QMessageBox::information(this, tr("Install Results"), install_results); 2221 QMessageBox::information(this, tr("Install Results"), install_results);
2214 void(Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / 2222 Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
2215 "game_list")); 2223 "game_list");
2216 game_list->PopulateAsync(UISettings::values.game_dirs); 2224 game_list->PopulateAsync(UISettings::values.game_dirs);
2217 ui.action_Install_File_NAND->setEnabled(true); 2225 ui.action_Install_File_NAND->setEnabled(true);
2218} 2226}
@@ -2844,7 +2852,7 @@ void GMainWindow::MigrateConfigFiles() {
2844 LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination); 2852 LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination);
2845 if (!Common::FS::RenameFile(origin, destination)) { 2853 if (!Common::FS::RenameFile(origin, destination)) {
2846 // Delete the old config file if one already exists in the new location. 2854 // Delete the old config file if one already exists in the new location.
2847 void(Common::FS::RemoveFile(origin)); 2855 Common::FS::RemoveFile(origin);
2848 } 2856 }
2849 } 2857 }
2850} 2858}
@@ -2856,7 +2864,8 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie
2856 const auto build_id = std::string(Common::g_build_id); 2864 const auto build_id = std::string(Common::g_build_id);
2857 2865
2858 const auto yuzu_title = fmt::format("yuzu | {}-{}", branch_name, description); 2866 const auto yuzu_title = fmt::format("yuzu | {}-{}", branch_name, description);
2859 const auto override_title = fmt::format(std::string(Common::g_title_bar_format_idle), build_id); 2867 const auto override_title =
2868 fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
2860 const auto window_title = override_title.empty() ? yuzu_title : override_title; 2869 const auto window_title = override_title.empty() ? yuzu_title : override_title;
2861 2870
2862 if (title_name.empty()) { 2871 if (title_name.empty()) {
@@ -3039,9 +3048,9 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
3039 3048
3040 const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); 3049 const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
3041 3050
3042 void(Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated")); 3051 Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated");
3043 void(Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated")); 3052 Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated");
3044 void(Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated")); 3053 Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated");
3045 } 3054 }
3046 3055
3047 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); 3056 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 621b31571..60bf66ec0 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -443,6 +443,8 @@ void Config::ReadValues() {
443 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", true)); 443 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", true));
444 Settings::values.use_vsync.SetValue( 444 Settings::values.use_vsync.SetValue(
445 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1))); 445 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
446 Settings::values.disable_fps_limit.SetValue(
447 sdl2_config->GetBoolean("Renderer", "disable_fps_limit", false));
446 Settings::values.use_assembly_shaders.SetValue( 448 Settings::values.use_assembly_shaders.SetValue(
447 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", true)); 449 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", true));
448 Settings::values.use_asynchronous_shaders.SetValue( 450 Settings::values.use_asynchronous_shaders.SetValue(
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 37d895ebd..cc9850aad 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -227,6 +227,10 @@ use_asynchronous_gpu_emulation =
227# 0: Off, 1 (default): On 227# 0: Off, 1 (default): On
228use_vsync = 228use_vsync =
229 229
230# Whether to use garbage collection or not for GPU caches.
231# 0 (default): Off, 1: On
232use_caches_gc =
233
230# The clear color for the renderer. What shows up on the sides of the bottom screen. 234# The clear color for the renderer. What shows up on the sides of the bottom screen.
231# Must be in range of 0.0-1.0. Defaults to 1.0 for all. 235# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
232bg_red = 236bg_red =
@@ -362,6 +366,9 @@ use_debug_asserts =
362use_auto_stub = 366use_auto_stub =
363# Enables/Disables the macro JIT compiler 367# Enables/Disables the macro JIT compiler
364disable_macro_jit=false 368disable_macro_jit=false
369# Presents guest frames as they become available. Experimental.
370# false: Disabled (default), true: Enabled
371disable_fps_limit=false
365 372
366[WebService] 373[WebService]
367# Whether or not to enable telemetry 374# Whether or not to enable telemetry
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 584967f5c..50e388312 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -219,7 +219,7 @@ int main(int argc, char** argv) {
219 system.GPU().Start(); 219 system.GPU().Start();
220 220
221 system.Renderer().ReadRasterizer()->LoadDiskResources( 221 system.Renderer().ReadRasterizer()->LoadDiskResources(
222 system.CurrentProcess()->GetTitleID(), false, 222 system.CurrentProcess()->GetTitleID(), std::stop_token{},
223 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); 223 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
224 224
225 void(system.Run()); 225 void(system.Run());