summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt5
-rw-r--r--src/audio_core/common.h2
-rw-r--r--src/audio_core/sdl2_sink.cpp163
-rw-r--r--src/audio_core/sdl2_sink.h29
-rw-r--r--src/audio_core/sink_context.h2
-rw-r--r--src/audio_core/sink_details.cpp10
-rw-r--r--src/audio_core/stream.cpp7
-rw-r--r--src/audio_core/stream.h6
-rw-r--r--src/common/CMakeLists.txt3
-rw-r--r--src/common/common_sizes.h43
-rw-r--r--src/common/detached_tasks.cpp2
-rw-r--r--src/common/fs/file.cpp14
-rw-r--r--src/common/fs/file.h15
-rw-r--r--src/common/fs/fs.cpp23
-rw-r--r--src/common/fs/fs.h30
-rw-r--r--src/common/hex_util.h3
-rw-r--r--src/common/host_memory.cpp2
-rw-r--r--src/common/literals.h31
-rw-r--r--src/common/logging/backend.cpp160
-rw-r--r--src/common/logging/backend.h43
-rw-r--r--src/common/logging/filter.cpp132
-rw-r--r--src/common/logging/filter.h12
-rw-r--r--src/common/logging/log.h120
-rw-r--r--src/common/logging/text_formatter.cpp2
-rw-r--r--src/common/logging/types.h144
-rw-r--r--src/common/settings.cpp5
-rw-r--r--src/common/settings.h5
-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/patch_manager.cpp24
-rw-r--r--src/core/file_sys/patch_manager.h3
-rw-r--r--src/core/file_sys/registered_cache.cpp13
-rw-r--r--src/core/file_sys/sdmc_factory.cpp31
-rw-r--r--src/core/file_sys/sdmc_factory.h6
-rw-r--r--src/core/file_sys/system_archive/system_version.cpp48
-rw-r--r--src/core/file_sys/vfs.cpp1
-rw-r--r--src/core/file_sys/vfs_libzip.cpp1
-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.cpp5
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp24
-rw-r--r--src/core/hle/service/filesystem/filesystem.h1
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp15
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h12
-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/hid/hid.cpp23
-rw-r--r--src/core/hle/service/hid/hid.h1
-rw-r--r--src/core/hle/service/lm/lm.cpp47
-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/core/reporter.cpp36
-rw-r--r--src/core/reporter.h9
-rw-r--r--src/core/telemetry_session.cpp1
-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.txt2
-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.comp48
-rw-r--r--src/video_core/rasterizer_interface.h4
-rw-r--r--src/video_core/renderer_base.h2
-rw-r--r--src/video_core/renderer_opengl/gl_arb_decompiler.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp58
-rw-r--r--src/video_core/renderer_opengl/gl_device.h3
-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.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h9
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h4
-rw-r--r--src/video_core/renderer_opengl/util_shaders.cpp31
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h4
-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.cpp13
-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.cpp25
-rw-r--r--src/video_core/textures/astc.cpp1579
-rw-r--r--src/video_core/textures/astc.h12
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_debug_callback.cpp8
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp33
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h41
-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.cpp19
-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_cpu_debug.ui2
-rw-r--r--src/yuzu/configuration/configure_debug.cpp11
-rw-r--r--src/yuzu/configuration/configure_debug.ui9
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp7
-rw-r--r--src/yuzu/configuration/configure_graphics.h1
-rw-r--r--src/yuzu/configuration/configure_graphics.ui7
-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/game_list.cpp12
-rw-r--r--src/yuzu/game_list.h7
-rw-r--r--src/yuzu/main.cpp61
-rw-r--r--src/yuzu/main.h7
-rw-r--r--src/yuzu_cmd/config.cpp10
-rw-r--r--src/yuzu_cmd/default_ini.h22
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
159 files changed, 4029 insertions, 972 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index a0ae07752..d25a1a645 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -42,6 +42,7 @@ add_library(audio_core STATIC
42 voice_context.h 42 voice_context.h
43 43
44 $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> 44 $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
45 $<$<BOOL:${ENABLE_SDL2}>:sdl2_sink.cpp sdl2_sink.h>
45) 46)
46 47
47create_target_directory_groups(audio_core) 48create_target_directory_groups(audio_core)
@@ -71,3 +72,7 @@ if(ENABLE_CUBEB)
71 target_link_libraries(audio_core PRIVATE cubeb) 72 target_link_libraries(audio_core PRIVATE cubeb)
72 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1) 73 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
73endif() 74endif()
75if(ENABLE_SDL2)
76 target_link_libraries(audio_core PRIVATE SDL2)
77 target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
78endif()
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/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp
new file mode 100644
index 000000000..62d3716a6
--- /dev/null
+++ b/src/audio_core/sdl2_sink.cpp
@@ -0,0 +1,163 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <atomic>
7#include <cstring>
8#include "audio_core/sdl2_sink.h"
9#include "audio_core/stream.h"
10#include "audio_core/time_stretch.h"
11#include "common/assert.h"
12#include "common/logging/log.h"
13//#include "common/settings.h"
14
15// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
16#ifdef __clang__
17#pragma clang diagnostic push
18#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
19#endif
20#include <SDL.h>
21#ifdef __clang__
22#pragma clang diagnostic pop
23#endif
24
25namespace AudioCore {
26
27class SDLSinkStream final : public SinkStream {
28public:
29 SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
30 : num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} {
31
32 SDL_AudioSpec spec;
33 spec.freq = sample_rate;
34 spec.channels = static_cast<u8>(num_channels);
35 spec.format = AUDIO_S16SYS;
36 spec.samples = 4096;
37 spec.callback = nullptr;
38
39 SDL_AudioSpec obtained;
40 if (output_device.empty()) {
41 dev = SDL_OpenAudioDevice(nullptr, 0, &spec, &obtained, 0);
42 } else {
43 dev = SDL_OpenAudioDevice(output_device.c_str(), 0, &spec, &obtained, 0);
44 }
45
46 if (dev == 0) {
47 LOG_CRITICAL(Audio_Sink, "Error opening sdl audio device: {}", SDL_GetError());
48 return;
49 }
50
51 SDL_PauseAudioDevice(dev, 0);
52 }
53
54 ~SDLSinkStream() override {
55 if (dev == 0) {
56 return;
57 }
58
59 SDL_CloseAudioDevice(dev);
60 }
61
62 void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override {
63 if (source_num_channels > num_channels) {
64 // Downsample 6 channels to 2
65 ASSERT_MSG(source_num_channels == 6, "Channel count must be 6");
66
67 std::vector<s16> buf;
68 buf.reserve(samples.size() * num_channels / source_num_channels);
69 for (std::size_t i = 0; i < samples.size(); i += source_num_channels) {
70 // Downmixing implementation taken from the ATSC standard
71 const s16 left{samples[i + 0]};
72 const s16 right{samples[i + 1]};
73 const s16 center{samples[i + 2]};
74 const s16 surround_left{samples[i + 4]};
75 const s16 surround_right{samples[i + 5]};
76 // Not used in the ATSC reference implementation
77 [[maybe_unused]] const s16 low_frequency_effects{samples[i + 3]};
78
79 constexpr s32 clev{707}; // center mixing level coefficient
80 constexpr s32 slev{707}; // surround mixing level coefficient
81
82 buf.push_back(static_cast<s16>(left + (clev * center / 1000) +
83 (slev * surround_left / 1000)));
84 buf.push_back(static_cast<s16>(right + (clev * center / 1000) +
85 (slev * surround_right / 1000)));
86 }
87 int ret = SDL_QueueAudio(dev, static_cast<const void*>(buf.data()),
88 static_cast<u32>(buf.size() * sizeof(s16)));
89 if (ret < 0)
90 LOG_WARNING(Audio_Sink, "Could not queue audio buffer: {}", SDL_GetError());
91 return;
92 }
93
94 int ret = SDL_QueueAudio(dev, static_cast<const void*>(samples.data()),
95 static_cast<u32>(samples.size() * sizeof(s16)));
96 if (ret < 0)
97 LOG_WARNING(Audio_Sink, "Could not queue audio buffer: {}", SDL_GetError());
98 }
99
100 std::size_t SamplesInQueue(u32 channel_count) const override {
101 if (dev == 0)
102 return 0;
103
104 return SDL_GetQueuedAudioSize(dev) / (channel_count * sizeof(s16));
105 }
106
107 void Flush() override {
108 should_flush = true;
109 }
110
111 u32 GetNumChannels() const {
112 return num_channels;
113 }
114
115private:
116 SDL_AudioDeviceID dev = 0;
117 u32 num_channels{};
118 std::atomic<bool> should_flush{};
119 TimeStretcher time_stretch;
120};
121
122SDLSink::SDLSink(std::string_view target_device_name) {
123 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
124 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
125 LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
126 return;
127 }
128 }
129
130 if (target_device_name != auto_device_name && !target_device_name.empty()) {
131 output_device = target_device_name;
132 } else {
133 output_device.clear();
134 }
135}
136
137SDLSink::~SDLSink() = default;
138
139SinkStream& SDLSink::AcquireSinkStream(u32 sample_rate, u32 num_channels, const std::string&) {
140 sink_streams.push_back(
141 std::make_unique<SDLSinkStream>(sample_rate, num_channels, output_device));
142 return *sink_streams.back();
143}
144
145std::vector<std::string> ListSDLSinkDevices() {
146 std::vector<std::string> device_list;
147
148 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
149 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
150 LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
151 return {};
152 }
153 }
154
155 const int device_count = SDL_GetNumAudioDevices(0);
156 for (int i = 0; i < device_count; ++i) {
157 device_list.emplace_back(SDL_GetAudioDeviceName(i, 0));
158 }
159
160 return device_list;
161}
162
163} // namespace AudioCore
diff --git a/src/audio_core/sdl2_sink.h b/src/audio_core/sdl2_sink.h
new file mode 100644
index 000000000..8ec1526d8
--- /dev/null
+++ b/src/audio_core/sdl2_sink.h
@@ -0,0 +1,29 @@
1// Copyright 2018 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 <string>
8#include <vector>
9
10#include "audio_core/sink.h"
11
12namespace AudioCore {
13
14class SDLSink final : public Sink {
15public:
16 explicit SDLSink(std::string_view device_id);
17 ~SDLSink() override;
18
19 SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
20 const std::string& name) override;
21
22private:
23 std::string output_device;
24 std::vector<SinkStreamPtr> sink_streams;
25};
26
27std::vector<std::string> ListSDLSinkDevices();
28
29} // namespace AudioCore
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/sink_details.cpp b/src/audio_core/sink_details.cpp
index a848eb1c9..de10aecd2 100644
--- a/src/audio_core/sink_details.cpp
+++ b/src/audio_core/sink_details.cpp
@@ -11,6 +11,9 @@
11#ifdef HAVE_CUBEB 11#ifdef HAVE_CUBEB
12#include "audio_core/cubeb_sink.h" 12#include "audio_core/cubeb_sink.h"
13#endif 13#endif
14#ifdef HAVE_SDL2
15#include "audio_core/sdl2_sink.h"
16#endif
14#include "common/logging/log.h" 17#include "common/logging/log.h"
15 18
16namespace AudioCore { 19namespace AudioCore {
@@ -36,6 +39,13 @@ constexpr SinkDetails sink_details[] = {
36 }, 39 },
37 &ListCubebSinkDevices}, 40 &ListCubebSinkDevices},
38#endif 41#endif
42#ifdef HAVE_SDL2
43 SinkDetails{"sdl2",
44 [](std::string_view device_id) -> std::unique_ptr<Sink> {
45 return std::make_unique<SDLSink>(device_id);
46 },
47 &ListSDLSinkDevices},
48#endif
39 SinkDetails{"null", 49 SinkDetails{"null",
40 [](std::string_view device_id) -> std::unique_ptr<Sink> { 50 [](std::string_view device_id) -> std::unique_ptr<Sink> {
41 return std::make_unique<NullSink>(device_id); 51 return std::make_unique<NullSink>(device_id);
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 97fbdcbf9..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
@@ -141,6 +141,7 @@ add_library(common STATIC
141 logging/log.h 141 logging/log.h
142 logging/text_formatter.cpp 142 logging/text_formatter.cpp
143 logging/text_formatter.h 143 logging/text_formatter.h
144 logging/types.h
144 lz4_compression.cpp 145 lz4_compression.cpp
145 lz4_compression.h 146 lz4_compression.h
146 math_util.h 147 math_util.h
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 9f3de1cb0..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,11 +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 (!Exists(path)) { 186 if (Exists(path) && !IsFile(path)) {
187 return WriteStringToFile(path, type, string);
188 }
189
190 if (!IsFile(path)) {
191 return 0; 187 return 0;
192 } 188 }
193 189
@@ -309,7 +305,11 @@ bool IOFile::Flush() const {
309 305
310 errno = 0; 306 errno = 0;
311 307
312 const auto flush_result = std::fflush(file) == 0; 308#ifdef _WIN32
309 const auto flush_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
310#else
311 const auto flush_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
312#endif
313 313
314 if (!flush_result) { 314 if (!flush_result) {
315 const auto ec = std::error_code{errno, std::generic_category()}; 315 const auto ec = std::error_code{errno, std::generic_category()};
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index 50e270c5b..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
@@ -71,8 +71,9 @@ template <typename Path>
71 71
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 an 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,8 +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 a file does not exist at path, WriteStringToFile is called instead. 99 * If a file does not exist at path, it creates and opens a new empty file for appending.
99 * If the filesystem object at path is not a file, this function returns 0. 100 * If the filesystem object at path exists and is not a regular file, this function returns 0.
100 * 101 *
101 * @param path Filesystem path 102 * @param path Filesystem path
102 * @param type File type 103 * @param type File type
@@ -395,11 +396,11 @@ public:
395 [[nodiscard]] size_t WriteString(std::span<const char> string) const; 396 [[nodiscard]] size_t WriteString(std::span<const char> string) const;
396 397
397 /** 398 /**
398 * 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.
399 * 400 *
400 * @returns True if the flush was successful, false otherwise. 401 * @returns True if the flush was successful, false otherwise.
401 */ 402 */
402 [[nodiscard]] bool Flush() const; 403 bool Flush() const;
403 404
404 /** 405 /**
405 * 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 d492480d9..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 }
@@ -321,7 +322,8 @@ bool RemoveDirContentsRecursively(const fs::path& path) {
321 322
322 std::error_code ec; 323 std::error_code ec;
323 324
324 for (const auto& entry : fs::recursive_directory_iterator(path, ec)) { 325 // TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
326 for (const auto& entry : fs::directory_iterator(path, ec)) {
325 if (ec) { 327 if (ec) {
326 LOG_ERROR(Common_Filesystem, 328 LOG_ERROR(Common_Filesystem,
327 "Failed to completely enumerate the directory at path={}, ec_message={}", 329 "Failed to completely enumerate the directory at path={}, ec_message={}",
@@ -337,6 +339,12 @@ bool RemoveDirContentsRecursively(const fs::path& path) {
337 PathToUTF8String(entry.path()), ec.message()); 339 PathToUTF8String(entry.path()), ec.message());
338 break; 340 break;
339 } 341 }
342
343 // TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
344 // recursive_directory_iterator throws an exception despite passing in a std::error_code.
345 if (entry.status().type() == fs::file_type::directory) {
346 return RemoveDirContentsRecursively(entry.path());
347 }
340 } 348 }
341 349
342 if (ec) { 350 if (ec) {
@@ -475,7 +483,8 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
475 483
476 std::error_code ec; 484 std::error_code ec;
477 485
478 for (const auto& entry : fs::recursive_directory_iterator(path, ec)) { 486 // TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
487 for (const auto& entry : fs::directory_iterator(path, ec)) {
479 if (ec) { 488 if (ec) {
480 break; 489 break;
481 } 490 }
@@ -495,6 +504,12 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
495 break; 504 break;
496 } 505 }
497 } 506 }
507
508 // TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
509 // recursive_directory_iterator throws an exception despite passing in a std::error_code.
510 if (entry.status().type() == fs::file_type::directory) {
511 IterateDirEntriesRecursively(entry.path(), callback, filter);
512 }
498 } 513 }
499 514
500 if (callback_error || ec) { 515 if (callback_error || ec) {
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/host_memory.cpp b/src/common/host_memory.cpp
index 8bd70abc7..2a5a7596c 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -34,7 +34,7 @@ constexpr size_t HugePageSize = 0x200000;
34 34
35// Manually imported for MinGW compatibility 35// Manually imported for MinGW compatibility
36#ifndef MEM_RESERVE_PLACEHOLDER 36#ifndef MEM_RESERVE_PLACEHOLDER
37#define MEM_RESERVE_PLACEHOLDER 0x0004000 37#define MEM_RESERVE_PLACEHOLDER 0x00040000
38#endif 38#endif
39#ifndef MEM_REPLACE_PLACEHOLDER 39#ifndef MEM_REPLACE_PLACEHOLDER
40#define MEM_REPLACE_PLACEHOLDER 0x00004000 40#define MEM_REPLACE_PLACEHOLDER 0x00004000
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 6aa8ac960..b6fa4affb 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -17,7 +17,10 @@
17#endif 17#endif
18 18
19#include "common/assert.h" 19#include "common/assert.h"
20#include "common/fs/file.h"
20#include "common/fs/fs.h" 21#include "common/fs/fs.h"
22#include "common/literals.h"
23
21#include "common/logging/backend.h" 24#include "common/logging/backend.h"
22#include "common/logging/log.h" 25#include "common/logging/log.h"
23#include "common/logging/text_formatter.h" 26#include "common/logging/text_formatter.h"
@@ -97,8 +100,8 @@ private:
97 write_logs(entry); 100 write_logs(entry);
98 } 101 }
99 102
100 // 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
101 // where a system is repeatedly spamming logs even on close. 104 // case where a system is repeatedly spamming logs even on close.
102 const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100; 105 const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
103 int logs_written = 0; 106 int logs_written = 0;
104 while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) { 107 while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
@@ -140,10 +143,14 @@ private:
140 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; 143 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
141}; 144};
142 145
146ConsoleBackend::~ConsoleBackend() = default;
147
143void ConsoleBackend::Write(const Entry& entry) { 148void ConsoleBackend::Write(const Entry& entry) {
144 PrintMessage(entry); 149 PrintMessage(entry);
145} 150}
146 151
152ColorConsoleBackend::~ColorConsoleBackend() = default;
153
147void ColorConsoleBackend::Write(const Entry& entry) { 154void ColorConsoleBackend::Write(const Entry& entry) {
148 PrintColoredMessage(entry); 155 PrintColoredMessage(entry);
149} 156}
@@ -154,19 +161,23 @@ FileBackend::FileBackend(const std::filesystem::path& filename) {
154 161
155 // Existence checks are done within the functions themselves. 162 // Existence checks are done within the functions themselves.
156 // We don't particularly care if these succeed or not. 163 // We don't particularly care if these succeed or not.
157 void(FS::RemoveFile(old_filename)); 164 FS::RemoveFile(old_filename);
158 void(FS::RenameFile(filename, old_filename)); 165 void(FS::RenameFile(filename, old_filename));
159 166
160 file = FS::IOFile(filename, FS::FileAccessMode::Write, FS::FileType::TextFile); 167 file =
168 std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
161} 169}
162 170
171FileBackend::~FileBackend() = default;
172
163void FileBackend::Write(const Entry& entry) { 173void FileBackend::Write(const Entry& entry) {
174 using namespace Common::Literals;
164 // 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
165 // know) 176 // know)
166 constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024; 177 constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
167 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024; 178 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
168 179
169 if (!file.IsOpen()) { 180 if (!file->IsOpen()) {
170 return; 181 return;
171 } 182 }
172 183
@@ -176,147 +187,20 @@ void FileBackend::Write(const Entry& entry) {
176 return; 187 return;
177 } 188 }
178 189
179 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n')); 190 bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
180 if (entry.log_level >= Level::Error) { 191 if (entry.log_level >= Level::Error) {
181 void(file.Flush()); 192 file->Flush();
182 } 193 }
183} 194}
184 195
196DebuggerBackend::~DebuggerBackend() = default;
197
185void DebuggerBackend::Write(const Entry& entry) { 198void DebuggerBackend::Write(const Entry& entry) {
186#ifdef _WIN32 199#ifdef _WIN32
187 ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); 200 ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
188#endif 201#endif
189} 202}
190 203
191/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
192#define ALL_LOG_CLASSES() \
193 CLS(Log) \
194 CLS(Common) \
195 SUB(Common, Filesystem) \
196 SUB(Common, Memory) \
197 CLS(Core) \
198 SUB(Core, ARM) \
199 SUB(Core, Timing) \
200 CLS(Config) \
201 CLS(Debug) \
202 SUB(Debug, Emulated) \
203 SUB(Debug, GPU) \
204 SUB(Debug, Breakpoint) \
205 SUB(Debug, GDBStub) \
206 CLS(Kernel) \
207 SUB(Kernel, SVC) \
208 CLS(Service) \
209 SUB(Service, ACC) \
210 SUB(Service, Audio) \
211 SUB(Service, AM) \
212 SUB(Service, AOC) \
213 SUB(Service, APM) \
214 SUB(Service, ARP) \
215 SUB(Service, BCAT) \
216 SUB(Service, BPC) \
217 SUB(Service, BGTC) \
218 SUB(Service, BTDRV) \
219 SUB(Service, BTM) \
220 SUB(Service, Capture) \
221 SUB(Service, ERPT) \
222 SUB(Service, ETicket) \
223 SUB(Service, EUPLD) \
224 SUB(Service, Fatal) \
225 SUB(Service, FGM) \
226 SUB(Service, Friend) \
227 SUB(Service, FS) \
228 SUB(Service, GRC) \
229 SUB(Service, HID) \
230 SUB(Service, IRS) \
231 SUB(Service, LBL) \
232 SUB(Service, LDN) \
233 SUB(Service, LDR) \
234 SUB(Service, LM) \
235 SUB(Service, Migration) \
236 SUB(Service, Mii) \
237 SUB(Service, MM) \
238 SUB(Service, NCM) \
239 SUB(Service, NFC) \
240 SUB(Service, NFP) \
241 SUB(Service, NIFM) \
242 SUB(Service, NIM) \
243 SUB(Service, NPNS) \
244 SUB(Service, NS) \
245 SUB(Service, NVDRV) \
246 SUB(Service, OLSC) \
247 SUB(Service, PCIE) \
248 SUB(Service, PCTL) \
249 SUB(Service, PCV) \
250 SUB(Service, PM) \
251 SUB(Service, PREPO) \
252 SUB(Service, PSC) \
253 SUB(Service, PSM) \
254 SUB(Service, SET) \
255 SUB(Service, SM) \
256 SUB(Service, SPL) \
257 SUB(Service, SSL) \
258 SUB(Service, TCAP) \
259 SUB(Service, Time) \
260 SUB(Service, USB) \
261 SUB(Service, VI) \
262 SUB(Service, WLAN) \
263 CLS(HW) \
264 SUB(HW, Memory) \
265 SUB(HW, LCD) \
266 SUB(HW, GPU) \
267 SUB(HW, AES) \
268 CLS(IPC) \
269 CLS(Frontend) \
270 CLS(Render) \
271 SUB(Render, Software) \
272 SUB(Render, OpenGL) \
273 SUB(Render, Vulkan) \
274 CLS(Audio) \
275 SUB(Audio, DSP) \
276 SUB(Audio, Sink) \
277 CLS(Input) \
278 CLS(Network) \
279 CLS(Loader) \
280 CLS(CheatEngine) \
281 CLS(Crypto) \
282 CLS(WebService)
283
284// GetClassName is a macro defined by Windows.h, grrr...
285const char* GetLogClassName(Class log_class) {
286 switch (log_class) {
287#define CLS(x) \
288 case Class::x: \
289 return #x;
290#define SUB(x, y) \
291 case Class::x##_##y: \
292 return #x "." #y;
293 ALL_LOG_CLASSES()
294#undef CLS
295#undef SUB
296 case Class::Count:
297 break;
298 }
299 return "Invalid";
300}
301
302const char* GetLevelName(Level log_level) {
303#define LVL(x) \
304 case Level::x: \
305 return #x
306 switch (log_level) {
307 LVL(Trace);
308 LVL(Debug);
309 LVL(Info);
310 LVL(Warning);
311 LVL(Error);
312 LVL(Critical);
313 case Level::Count:
314 break;
315 }
316#undef LVL
317 return "Invalid";
318}
319
320void SetGlobalFilter(const Filter& filter) { 204void SetGlobalFilter(const Filter& filter) {
321 Impl::Instance().SetGlobalFilter(filter); 205 Impl::Instance().SetGlobalFilter(filter);
322} 206}
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index eb629a33f..4b9a910c1 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -1,43 +1,32 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
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#pragma once 5#pragma once
5 6
6#include <chrono>
7#include <filesystem> 7#include <filesystem>
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include <string_view> 10#include <string_view>
11#include "common/fs/file.h"
12#include "common/logging/filter.h" 11#include "common/logging/filter.h"
13#include "common/logging/log.h" 12#include "common/logging/log.h"
14 13
14namespace Common::FS {
15class IOFile;
16}
17
15namespace Common::Log { 18namespace Common::Log {
16 19
17class Filter; 20class Filter;
18 21
19/** 22/**
20 * A log entry. Log entries are store in a structured format to permit more varied output
21 * formatting on different frontends, as well as facilitating filtering and aggregation.
22 */
23struct Entry {
24 std::chrono::microseconds timestamp;
25 Class log_class{};
26 Level log_level{};
27 const char* filename = nullptr;
28 unsigned int line_num = 0;
29 std::string function;
30 std::string message;
31 bool final_entry = false;
32};
33
34/**
35 * Interface for logging backends. As loggers can be created and removed at runtime, this can be 23 * Interface for logging backends. As loggers can be created and removed at runtime, this can be
36 * used by a frontend for adding a custom logging backend as needed 24 * used by a frontend for adding a custom logging backend as needed
37 */ 25 */
38class Backend { 26class Backend {
39public: 27public:
40 virtual ~Backend() = default; 28 virtual ~Backend() = default;
29
41 virtual void SetFilter(const Filter& new_filter) { 30 virtual void SetFilter(const Filter& new_filter) {
42 filter = new_filter; 31 filter = new_filter;
43 } 32 }
@@ -53,6 +42,8 @@ private:
53 */ 42 */
54class ConsoleBackend : public Backend { 43class ConsoleBackend : public Backend {
55public: 44public:
45 ~ConsoleBackend() override;
46
56 static const char* Name() { 47 static const char* Name() {
57 return "console"; 48 return "console";
58 } 49 }
@@ -67,6 +58,8 @@ public:
67 */ 58 */
68class ColorConsoleBackend : public Backend { 59class ColorConsoleBackend : public Backend {
69public: 60public:
61 ~ColorConsoleBackend() override;
62
70 static const char* Name() { 63 static const char* Name() {
71 return "color_console"; 64 return "color_console";
72 } 65 }
@@ -83,6 +76,7 @@ public:
83class FileBackend : public Backend { 76class FileBackend : public Backend {
84public: 77public:
85 explicit FileBackend(const std::filesystem::path& filename); 78 explicit FileBackend(const std::filesystem::path& filename);
79 ~FileBackend() override;
86 80
87 static const char* Name() { 81 static const char* Name() {
88 return "file"; 82 return "file";
@@ -95,7 +89,7 @@ public:
95 void Write(const Entry& entry) override; 89 void Write(const Entry& entry) override;
96 90
97private: 91private:
98 FS::IOFile file; 92 std::unique_ptr<FS::IOFile> file;
99 std::size_t bytes_written = 0; 93 std::size_t bytes_written = 0;
100}; 94};
101 95
@@ -104,6 +98,8 @@ private:
104 */ 98 */
105class DebuggerBackend : public Backend { 99class DebuggerBackend : public Backend {
106public: 100public:
101 ~DebuggerBackend() override;
102
107 static const char* Name() { 103 static const char* Name() {
108 return "debugger"; 104 return "debugger";
109 } 105 }
@@ -120,17 +116,6 @@ void RemoveBackend(std::string_view backend_name);
120Backend* GetBackend(std::string_view backend_name); 116Backend* GetBackend(std::string_view backend_name);
121 117
122/** 118/**
123 * Returns the name of the passed log class as a C-string. Subclasses are separated by periods
124 * instead of underscores as in the enumeration.
125 */
126const char* GetLogClassName(Class log_class);
127
128/**
129 * Returns the name of the passed log level as a C-string.
130 */
131const char* GetLevelName(Level log_level);
132
133/**
134 * The global filter will prevent any messages from even being processed if they are filtered. Each 119 * The global filter will prevent any messages from even being processed if they are filtered. Each
135 * backend can have a filter, but if the level is lower than the global filter, the backend will 120 * backend can have a filter, but if the level is lower than the global filter, the backend will
136 * never get the message 121 * never get the message
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 20a2dd106..4f2cc29e1 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include "common/logging/backend.h"
7#include "common/logging/filter.h" 6#include "common/logging/filter.h"
8#include "common/string_util.h" 7#include "common/string_util.h"
9 8
@@ -22,7 +21,7 @@ Level GetLevelByName(const It begin, const It end) {
22 21
23template <typename It> 22template <typename It>
24Class GetClassByName(const It begin, const It end) { 23Class GetClassByName(const It begin, const It end) {
25 for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) { 24 for (u8 i = 0; i < static_cast<u8>(Class::Count); ++i) {
26 const char* level_name = GetLogClassName(static_cast<Class>(i)); 25 const char* level_name = GetLogClassName(static_cast<Class>(i));
27 if (Common::ComparePartialString(begin, end, level_name)) { 26 if (Common::ComparePartialString(begin, end, level_name)) {
28 return static_cast<Class>(i); 27 return static_cast<Class>(i);
@@ -62,6 +61,135 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
62} 61}
63} // Anonymous namespace 62} // Anonymous namespace
64 63
64/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
65#define ALL_LOG_CLASSES() \
66 CLS(Log) \
67 CLS(Common) \
68 SUB(Common, Filesystem) \
69 SUB(Common, Memory) \
70 CLS(Core) \
71 SUB(Core, ARM) \
72 SUB(Core, Timing) \
73 CLS(Config) \
74 CLS(Debug) \
75 SUB(Debug, Emulated) \
76 SUB(Debug, GPU) \
77 SUB(Debug, Breakpoint) \
78 SUB(Debug, GDBStub) \
79 CLS(Kernel) \
80 SUB(Kernel, SVC) \
81 CLS(Service) \
82 SUB(Service, ACC) \
83 SUB(Service, Audio) \
84 SUB(Service, AM) \
85 SUB(Service, AOC) \
86 SUB(Service, APM) \
87 SUB(Service, ARP) \
88 SUB(Service, BCAT) \
89 SUB(Service, BPC) \
90 SUB(Service, BGTC) \
91 SUB(Service, BTDRV) \
92 SUB(Service, BTM) \
93 SUB(Service, Capture) \
94 SUB(Service, ERPT) \
95 SUB(Service, ETicket) \
96 SUB(Service, EUPLD) \
97 SUB(Service, Fatal) \
98 SUB(Service, FGM) \
99 SUB(Service, Friend) \
100 SUB(Service, FS) \
101 SUB(Service, GRC) \
102 SUB(Service, HID) \
103 SUB(Service, IRS) \
104 SUB(Service, LBL) \
105 SUB(Service, LDN) \
106 SUB(Service, LDR) \
107 SUB(Service, LM) \
108 SUB(Service, Migration) \
109 SUB(Service, Mii) \
110 SUB(Service, MM) \
111 SUB(Service, NCM) \
112 SUB(Service, NFC) \
113 SUB(Service, NFP) \
114 SUB(Service, NIFM) \
115 SUB(Service, NIM) \
116 SUB(Service, NPNS) \
117 SUB(Service, NS) \
118 SUB(Service, NVDRV) \
119 SUB(Service, OLSC) \
120 SUB(Service, PCIE) \
121 SUB(Service, PCTL) \
122 SUB(Service, PCV) \
123 SUB(Service, PM) \
124 SUB(Service, PREPO) \
125 SUB(Service, PSC) \
126 SUB(Service, PSM) \
127 SUB(Service, SET) \
128 SUB(Service, SM) \
129 SUB(Service, SPL) \
130 SUB(Service, SSL) \
131 SUB(Service, TCAP) \
132 SUB(Service, Time) \
133 SUB(Service, USB) \
134 SUB(Service, VI) \
135 SUB(Service, WLAN) \
136 CLS(HW) \
137 SUB(HW, Memory) \
138 SUB(HW, LCD) \
139 SUB(HW, GPU) \
140 SUB(HW, AES) \
141 CLS(IPC) \
142 CLS(Frontend) \
143 CLS(Render) \
144 SUB(Render, Software) \
145 SUB(Render, OpenGL) \
146 SUB(Render, Vulkan) \
147 CLS(Audio) \
148 SUB(Audio, DSP) \
149 SUB(Audio, Sink) \
150 CLS(Input) \
151 CLS(Network) \
152 CLS(Loader) \
153 CLS(CheatEngine) \
154 CLS(Crypto) \
155 CLS(WebService)
156
157// GetClassName is a macro defined by Windows.h, grrr...
158const char* GetLogClassName(Class log_class) {
159 switch (log_class) {
160#define CLS(x) \
161 case Class::x: \
162 return #x;
163#define SUB(x, y) \
164 case Class::x##_##y: \
165 return #x "." #y;
166 ALL_LOG_CLASSES()
167#undef CLS
168#undef SUB
169 case Class::Count:
170 break;
171 }
172 return "Invalid";
173}
174
175const char* GetLevelName(Level log_level) {
176#define LVL(x) \
177 case Level::x: \
178 return #x
179 switch (log_level) {
180 LVL(Trace);
181 LVL(Debug);
182 LVL(Info);
183 LVL(Warning);
184 LVL(Error);
185 LVL(Critical);
186 case Level::Count:
187 break;
188 }
189#undef LVL
190 return "Invalid";
191}
192
65Filter::Filter(Level default_level) { 193Filter::Filter(Level default_level) {
66 ResetAll(default_level); 194 ResetAll(default_level);
67} 195}
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h
index f5673a9f6..1a3074e04 100644
--- a/src/common/logging/filter.h
+++ b/src/common/logging/filter.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <chrono>
8#include <cstddef> 9#include <cstddef>
9#include <string_view> 10#include <string_view>
10#include "common/logging/log.h" 11#include "common/logging/log.h"
@@ -12,6 +13,17 @@
12namespace Common::Log { 13namespace Common::Log {
13 14
14/** 15/**
16 * Returns the name of the passed log class as a C-string. Subclasses are separated by periods
17 * instead of underscores as in the enumeration.
18 */
19const char* GetLogClassName(Class log_class);
20
21/**
22 * Returns the name of the passed log level as a C-string.
23 */
24const char* GetLevelName(Level log_level);
25
26/**
15 * Implements a log message filter which allows different log classes to have different minimum 27 * Implements a log message filter which allows different log classes to have different minimum
16 * severity levels. The filter can be changed at runtime and can be parsed from a string to allow 28 * severity levels. The filter can be changed at runtime and can be parsed from a string to allow
17 * editing via the interface or loading from a configuration file. 29 * editing via the interface or loading from a configuration file.
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 1f0f8db52..8d43eddc7 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -5,7 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <fmt/format.h> 7#include <fmt/format.h>
8#include "common/common_types.h" 8#include "common/logging/types.h"
9 9
10namespace Common::Log { 10namespace Common::Log {
11 11
@@ -18,124 +18,6 @@ constexpr const char* TrimSourcePath(std::string_view source) {
18 return source.data() + idx; 18 return source.data() + idx;
19} 19}
20 20
21/// Specifies the severity or level of detail of the log message.
22enum class Level : u8 {
23 Trace, ///< Extremely detailed and repetitive debugging information that is likely to
24 ///< pollute logs.
25 Debug, ///< Less detailed debugging information.
26 Info, ///< Status information from important points during execution.
27 Warning, ///< Minor or potential problems found during execution of a task.
28 Error, ///< Major problems found during execution of a task that prevent it from being
29 ///< completed.
30 Critical, ///< Major problems during execution that threaten the stability of the entire
31 ///< application.
32
33 Count ///< Total number of logging levels
34};
35
36typedef u8 ClassType;
37
38/**
39 * Specifies the sub-system that generated the log message.
40 *
41 * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in
42 * backend.cpp.
43 */
44enum class Class : ClassType {
45 Log, ///< Messages about the log system itself
46 Common, ///< Library routines
47 Common_Filesystem, ///< Filesystem interface library
48 Common_Memory, ///< Memory mapping and management functions
49 Core, ///< LLE emulation core
50 Core_ARM, ///< ARM CPU core
51 Core_Timing, ///< CoreTiming functions
52 Config, ///< Emulator configuration (including commandline)
53 Debug, ///< Debugging tools
54 Debug_Emulated, ///< Debug messages from the emulated programs
55 Debug_GPU, ///< GPU debugging tools
56 Debug_Breakpoint, ///< Logging breakpoints and watchpoints
57 Debug_GDBStub, ///< GDB Stub
58 Kernel, ///< The HLE implementation of the CTR kernel
59 Kernel_SVC, ///< Kernel system calls
60 Service, ///< HLE implementation of system services. Each major service
61 ///< should have its own subclass.
62 Service_ACC, ///< The ACC (Accounts) service
63 Service_AM, ///< The AM (Applet manager) service
64 Service_AOC, ///< The AOC (AddOn Content) service
65 Service_APM, ///< The APM (Performance) service
66 Service_ARP, ///< The ARP service
67 Service_Audio, ///< The Audio (Audio control) service
68 Service_BCAT, ///< The BCAT service
69 Service_BGTC, ///< The BGTC (Background Task Controller) service
70 Service_BPC, ///< The BPC service
71 Service_BTDRV, ///< The Bluetooth driver service
72 Service_BTM, ///< The BTM service
73 Service_Capture, ///< The capture service
74 Service_ERPT, ///< The error reporting service
75 Service_ETicket, ///< The ETicket service
76 Service_EUPLD, ///< The error upload service
77 Service_Fatal, ///< The Fatal service
78 Service_FGM, ///< The FGM service
79 Service_Friend, ///< The friend service
80 Service_FS, ///< The FS (Filesystem) service
81 Service_GRC, ///< The game recording service
82 Service_HID, ///< The HID (Human interface device) service
83 Service_IRS, ///< The IRS service
84 Service_LBL, ///< The LBL (LCD backlight) service
85 Service_LDN, ///< The LDN (Local domain network) service
86 Service_LDR, ///< The loader service
87 Service_LM, ///< The LM (Logger) service
88 Service_Migration, ///< The migration service
89 Service_Mii, ///< The Mii service
90 Service_MM, ///< The MM (Multimedia) service
91 Service_NCM, ///< The NCM service
92 Service_NFC, ///< The NFC (Near-field communication) service
93 Service_NFP, ///< The NFP service
94 Service_NIFM, ///< The NIFM (Network interface) service
95 Service_NIM, ///< The NIM service
96 Service_NPNS, ///< The NPNS service
97 Service_NS, ///< The NS services
98 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
99 Service_OLSC, ///< The OLSC service
100 Service_PCIE, ///< The PCIe service
101 Service_PCTL, ///< The PCTL (Parental control) service
102 Service_PCV, ///< The PCV service
103 Service_PM, ///< The PM service
104 Service_PREPO, ///< The PREPO (Play report) service
105 Service_PSC, ///< The PSC service
106 Service_PSM, ///< The PSM service
107 Service_SET, ///< The SET (Settings) service
108 Service_SM, ///< The SM (Service manager) service
109 Service_SPL, ///< The SPL service
110 Service_SSL, ///< The SSL service
111 Service_TCAP, ///< The TCAP service.
112 Service_Time, ///< The time service
113 Service_USB, ///< The USB (Universal Serial Bus) service
114 Service_VI, ///< The VI (Video interface) service
115 Service_WLAN, ///< The WLAN (Wireless local area network) service
116 HW, ///< Low-level hardware emulation
117 HW_Memory, ///< Memory-map and address translation
118 HW_LCD, ///< LCD register emulation
119 HW_GPU, ///< GPU control emulation
120 HW_AES, ///< AES engine emulation
121 IPC, ///< IPC interface
122 Frontend, ///< Emulator UI
123 Render, ///< Emulator video output and hardware acceleration
124 Render_Software, ///< Software renderer backend
125 Render_OpenGL, ///< OpenGL backend
126 Render_Vulkan, ///< Vulkan backend
127 Audio, ///< Audio emulation
128 Audio_DSP, ///< The HLE implementation of the DSP
129 Audio_Sink, ///< Emulator audio output backend
130 Loader, ///< ROM loader
131 CheatEngine, ///< Memory manipulation and engine VM functions
132 Crypto, ///< Cryptographic engine/functions
133 Input, ///< Input emulation
134 Network, ///< Network emulation
135 WebService, ///< Interface to yuzu Web Services
136 Count ///< Total number of logging classes
137};
138
139/// Logs a message to the global logger, using fmt 21/// Logs a message to the global logger, using fmt
140void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, 22void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
141 unsigned int line_num, const char* function, const char* format, 23 unsigned int line_num, const char* function, const char* format,
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
index 80ee2cca1..cfc0d5846 100644
--- a/src/common/logging/text_formatter.cpp
+++ b/src/common/logging/text_formatter.cpp
@@ -11,7 +11,7 @@
11 11
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_funcs.h" 13#include "common/common_funcs.h"
14#include "common/logging/backend.h" 14#include "common/logging/filter.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/logging/text_formatter.h" 16#include "common/logging/text_formatter.h"
17#include "common/string_util.h" 17#include "common/string_util.h"
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
new file mode 100644
index 000000000..88b0e9c01
--- /dev/null
+++ b/src/common/logging/types.h
@@ -0,0 +1,144 @@
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 <chrono>
8
9#include "common/common_types.h"
10
11namespace Common::Log {
12
13/// Specifies the severity or level of detail of the log message.
14enum class Level : u8 {
15 Trace, ///< Extremely detailed and repetitive debugging information that is likely to
16 ///< pollute logs.
17 Debug, ///< Less detailed debugging information.
18 Info, ///< Status information from important points during execution.
19 Warning, ///< Minor or potential problems found during execution of a task.
20 Error, ///< Major problems found during execution of a task that prevent it from being
21 ///< completed.
22 Critical, ///< Major problems during execution that threaten the stability of the entire
23 ///< application.
24
25 Count ///< Total number of logging levels
26};
27
28/**
29 * Specifies the sub-system that generated the log message.
30 *
31 * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in
32 * filter.cpp.
33 */
34enum class Class : u8 {
35 Log, ///< Messages about the log system itself
36 Common, ///< Library routines
37 Common_Filesystem, ///< Filesystem interface library
38 Common_Memory, ///< Memory mapping and management functions
39 Core, ///< LLE emulation core
40 Core_ARM, ///< ARM CPU core
41 Core_Timing, ///< CoreTiming functions
42 Config, ///< Emulator configuration (including commandline)
43 Debug, ///< Debugging tools
44 Debug_Emulated, ///< Debug messages from the emulated programs
45 Debug_GPU, ///< GPU debugging tools
46 Debug_Breakpoint, ///< Logging breakpoints and watchpoints
47 Debug_GDBStub, ///< GDB Stub
48 Kernel, ///< The HLE implementation of the CTR kernel
49 Kernel_SVC, ///< Kernel system calls
50 Service, ///< HLE implementation of system services. Each major service
51 ///< should have its own subclass.
52 Service_ACC, ///< The ACC (Accounts) service
53 Service_AM, ///< The AM (Applet manager) service
54 Service_AOC, ///< The AOC (AddOn Content) service
55 Service_APM, ///< The APM (Performance) service
56 Service_ARP, ///< The ARP service
57 Service_Audio, ///< The Audio (Audio control) service
58 Service_BCAT, ///< The BCAT service
59 Service_BGTC, ///< The BGTC (Background Task Controller) service
60 Service_BPC, ///< The BPC service
61 Service_BTDRV, ///< The Bluetooth driver service
62 Service_BTM, ///< The BTM service
63 Service_Capture, ///< The capture service
64 Service_ERPT, ///< The error reporting service
65 Service_ETicket, ///< The ETicket service
66 Service_EUPLD, ///< The error upload service
67 Service_Fatal, ///< The Fatal service
68 Service_FGM, ///< The FGM service
69 Service_Friend, ///< The friend service
70 Service_FS, ///< The FS (Filesystem) service
71 Service_GRC, ///< The game recording service
72 Service_HID, ///< The HID (Human interface device) service
73 Service_IRS, ///< The IRS service
74 Service_LBL, ///< The LBL (LCD backlight) service
75 Service_LDN, ///< The LDN (Local domain network) service
76 Service_LDR, ///< The loader service
77 Service_LM, ///< The LM (Logger) service
78 Service_Migration, ///< The migration service
79 Service_Mii, ///< The Mii service
80 Service_MM, ///< The MM (Multimedia) service
81 Service_NCM, ///< The NCM service
82 Service_NFC, ///< The NFC (Near-field communication) service
83 Service_NFP, ///< The NFP service
84 Service_NIFM, ///< The NIFM (Network interface) service
85 Service_NIM, ///< The NIM service
86 Service_NPNS, ///< The NPNS service
87 Service_NS, ///< The NS services
88 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
89 Service_OLSC, ///< The OLSC service
90 Service_PCIE, ///< The PCIe service
91 Service_PCTL, ///< The PCTL (Parental control) service
92 Service_PCV, ///< The PCV service
93 Service_PM, ///< The PM service
94 Service_PREPO, ///< The PREPO (Play report) service
95 Service_PSC, ///< The PSC service
96 Service_PSM, ///< The PSM service
97 Service_SET, ///< The SET (Settings) service
98 Service_SM, ///< The SM (Service manager) service
99 Service_SPL, ///< The SPL service
100 Service_SSL, ///< The SSL service
101 Service_TCAP, ///< The TCAP service.
102 Service_Time, ///< The time service
103 Service_USB, ///< The USB (Universal Serial Bus) service
104 Service_VI, ///< The VI (Video interface) service
105 Service_WLAN, ///< The WLAN (Wireless local area network) service
106 HW, ///< Low-level hardware emulation
107 HW_Memory, ///< Memory-map and address translation
108 HW_LCD, ///< LCD register emulation
109 HW_GPU, ///< GPU control emulation
110 HW_AES, ///< AES engine emulation
111 IPC, ///< IPC interface
112 Frontend, ///< Emulator UI
113 Render, ///< Emulator video output and hardware acceleration
114 Render_Software, ///< Software renderer backend
115 Render_OpenGL, ///< OpenGL backend
116 Render_Vulkan, ///< Vulkan backend
117 Audio, ///< Audio emulation
118 Audio_DSP, ///< The HLE implementation of the DSP
119 Audio_Sink, ///< Emulator audio output backend
120 Loader, ///< ROM loader
121 CheatEngine, ///< Memory manipulation and engine VM functions
122 Crypto, ///< Cryptographic engine/functions
123 Input, ///< Input emulation
124 Network, ///< Network emulation
125 WebService, ///< Interface to yuzu Web Services
126 Count ///< Total number of logging classes
127};
128
129/**
130 * A log entry. Log entries are store in a structured format to permit more varied output
131 * formatting on different frontends, as well as facilitating filtering and aggregation.
132 */
133struct Entry {
134 std::chrono::microseconds timestamp;
135 Class log_class{};
136 Level log_level{};
137 const char* filename = nullptr;
138 unsigned int line_num = 0;
139 std::string function;
140 std::string message;
141 bool final_entry = false;
142};
143
144} // namespace Common::Log
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 360e878d6..e1bb4b7ff 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -55,9 +55,11 @@ void LogSettings() {
55 log_setting("Renderer_UseAsynchronousGpuEmulation", 55 log_setting("Renderer_UseAsynchronousGpuEmulation",
56 values.use_asynchronous_gpu_emulation.GetValue()); 56 values.use_asynchronous_gpu_emulation.GetValue());
57 log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue()); 57 log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue());
58 log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
58 log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); 59 log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
59 log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue()); 60 log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
60 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());
61 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); 63 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
62 log_setting("Audio_OutputEngine", values.sink_id); 64 log_setting("Audio_OutputEngine", values.sink_id);
63 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); 65 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
@@ -121,6 +123,7 @@ void RestoreGlobalState(bool is_powered_on) {
121 values.cpu_accuracy.SetGlobal(true); 123 values.cpu_accuracy.SetGlobal(true);
122 values.cpuopt_unsafe_unfuse_fma.SetGlobal(true); 124 values.cpuopt_unsafe_unfuse_fma.SetGlobal(true);
123 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);
124 values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true); 127 values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
125 values.cpuopt_unsafe_fastmem_check.SetGlobal(true); 128 values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
126 129
@@ -135,10 +138,12 @@ void RestoreGlobalState(bool is_powered_on) {
135 values.gpu_accuracy.SetGlobal(true); 138 values.gpu_accuracy.SetGlobal(true);
136 values.use_asynchronous_gpu_emulation.SetGlobal(true); 139 values.use_asynchronous_gpu_emulation.SetGlobal(true);
137 values.use_nvdec_emulation.SetGlobal(true); 140 values.use_nvdec_emulation.SetGlobal(true);
141 values.accelerate_astc.SetGlobal(true);
138 values.use_vsync.SetGlobal(true); 142 values.use_vsync.SetGlobal(true);
139 values.use_assembly_shaders.SetGlobal(true); 143 values.use_assembly_shaders.SetGlobal(true);
140 values.use_asynchronous_shaders.SetGlobal(true); 144 values.use_asynchronous_shaders.SetGlobal(true);
141 values.use_fast_gpu_time.SetGlobal(true); 145 values.use_fast_gpu_time.SetGlobal(true);
146 values.use_caches_gc.SetGlobal(true);
142 values.bg_red.SetGlobal(true); 147 values.bg_red.SetGlobal(true);
143 values.bg_green.SetGlobal(true); 148 values.bg_green.SetGlobal(true);
144 values.bg_blue.SetGlobal(true); 149 values.bg_blue.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 1af8c5ac2..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
@@ -147,10 +148,13 @@ struct Values {
147 Setting<GPUAccuracy> gpu_accuracy; 148 Setting<GPUAccuracy> gpu_accuracy;
148 Setting<bool> use_asynchronous_gpu_emulation; 149 Setting<bool> use_asynchronous_gpu_emulation;
149 Setting<bool> use_nvdec_emulation; 150 Setting<bool> use_nvdec_emulation;
151 Setting<bool> accelerate_astc;
150 Setting<bool> use_vsync; 152 Setting<bool> use_vsync;
153 Setting<bool> disable_fps_limit;
151 Setting<bool> use_assembly_shaders; 154 Setting<bool> use_assembly_shaders;
152 Setting<bool> use_asynchronous_shaders; 155 Setting<bool> use_asynchronous_shaders;
153 Setting<bool> use_fast_gpu_time; 156 Setting<bool> use_fast_gpu_time;
157 Setting<bool> use_caches_gc;
154 158
155 Setting<float> bg_red; 159 Setting<float> bg_red;
156 Setting<float> bg_green; 160 Setting<float> bg_green;
@@ -218,6 +222,7 @@ struct Values {
218 std::string program_args; 222 std::string program_args;
219 bool dump_exefs; 223 bool dump_exefs;
220 bool dump_nso; 224 bool dump_nso;
225 bool enable_fs_access_log;
221 bool reporting_services; 226 bool reporting_services;
222 bool quest_flag; 227 bool quest_flag;
223 bool disable_macro_jit; 228 bool disable_macro_jit;
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/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 53b8b7ca0..7c0950bb0 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -345,8 +345,10 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
345static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type, 345static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
346 const Service::FileSystem::FileSystemController& fs_controller) { 346 const Service::FileSystem::FileSystemController& fs_controller) {
347 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); 347 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
348 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
348 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 349 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
349 load_dir == nullptr || load_dir->GetSize() <= 0) { 350 ((load_dir == nullptr || load_dir->GetSize() <= 0) &&
351 (sdmc_load_dir == nullptr || sdmc_load_dir->GetSize() <= 0))) {
350 return; 352 return;
351 } 353 }
352 354
@@ -356,7 +358,10 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
356 } 358 }
357 359
358 const auto& disabled = Settings::values.disabled_addons[title_id]; 360 const auto& disabled = Settings::values.disabled_addons[title_id];
359 auto patch_dirs = load_dir->GetSubdirectories(); 361 std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
362 if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
363 patch_dirs.push_back(sdmc_load_dir);
364 }
360 std::sort(patch_dirs.begin(), patch_dirs.end(), 365 std::sort(patch_dirs.begin(), patch_dirs.end(),
361 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 366 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
362 367
@@ -402,7 +407,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
402} 407}
403 408
404VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, 409VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
405 VirtualFile update_raw) const { 410 VirtualFile update_raw, bool apply_layeredfs) const {
406 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}", 411 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
407 title_id, static_cast<u8>(type)); 412 title_id, static_cast<u8>(type));
408 413
@@ -442,7 +447,9 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
442 } 447 }
443 448
444 // LayeredFS 449 // LayeredFS
445 ApplyLayeredFS(romfs, title_id, type, fs_controller); 450 if (apply_layeredfs) {
451 ApplyLayeredFS(romfs, title_id, type, fs_controller);
452 }
446 453
447 return romfs; 454 return romfs;
448} 455}
@@ -524,6 +531,15 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
524 } 531 }
525 } 532 }
526 533
534 // SDMC mod directory (RomFS LayeredFS)
535 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
536 if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 &&
537 IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
538 const auto mod_disabled =
539 std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
540 out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS");
541 }
542
527 // DLC 543 // DLC
528 const auto dlc_entries = 544 const auto dlc_entries =
529 content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); 545 content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index fb1853035..3be871f35 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -64,7 +64,8 @@ public:
64 // - LayeredFS 64 // - LayeredFS
65 [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, 65 [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
66 ContentRecordType type = ContentRecordType::Program, 66 ContentRecordType type = ContentRecordType::Program,
67 VirtualFile update_raw = nullptr) const; 67 VirtualFile update_raw = nullptr,
68 bool apply_layeredfs = true) const;
68 69
69 // Returns a vector of pairs between patch names and patch versions. 70 // Returns a vector of pairs between patch names and patch versions.
70 // i.e. Update 3.2.2 will return {"Update", "3.2.2"} 71 // i.e. Update 3.2.2 will return {"Update", "3.2.2"}
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/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index cb56d8f2d..e5c72cd4d 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -12,23 +12,32 @@ namespace FileSys {
12 12
13constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB 13constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB
14 14
15SDMCFactory::SDMCFactory(VirtualDir dir_) 15SDMCFactory::SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_)
16 : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>( 16 : sd_dir(std::move(sd_dir_)), sd_mod_dir(std::move(sd_mod_dir_)),
17 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), 17 contents(std::make_unique<RegisteredCache>(
18 [](const VirtualFile& file, const NcaID& id) { 18 GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Contents/registered"),
19 return NAX{file, id}.GetDecrypted(); 19 [](const VirtualFile& file, const NcaID& id) {
20 })), 20 return NAX{file, id}.GetDecrypted();
21 })),
21 placeholder(std::make_unique<PlaceholderCache>( 22 placeholder(std::make_unique<PlaceholderCache>(
22 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {} 23 GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Contents/placehld"))) {}
23 24
24SDMCFactory::~SDMCFactory() = default; 25SDMCFactory::~SDMCFactory() = default;
25 26
26ResultVal<VirtualDir> SDMCFactory::Open() const { 27ResultVal<VirtualDir> SDMCFactory::Open() const {
27 return MakeResult<VirtualDir>(dir); 28 return MakeResult<VirtualDir>(sd_dir);
29}
30
31VirtualDir SDMCFactory::GetSDMCModificationLoadRoot(u64 title_id) const {
32 // LayeredFS doesn't work on updates and title id-less homebrew
33 if (title_id == 0 || (title_id & 0xFFF) == 0x800) {
34 return nullptr;
35 }
36 return GetOrCreateDirectoryRelative(sd_mod_dir, fmt::format("/{:016X}", title_id));
28} 37}
29 38
30VirtualDir SDMCFactory::GetSDMCContentDirectory() const { 39VirtualDir SDMCFactory::GetSDMCContentDirectory() const {
31 return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents"); 40 return GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Contents");
32} 41}
33 42
34RegisteredCache* SDMCFactory::GetSDMCContents() const { 43RegisteredCache* SDMCFactory::GetSDMCContents() const {
@@ -40,11 +49,11 @@ PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const {
40} 49}
41 50
42VirtualDir SDMCFactory::GetImageDirectory() const { 51VirtualDir SDMCFactory::GetImageDirectory() const {
43 return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album"); 52 return GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Album");
44} 53}
45 54
46u64 SDMCFactory::GetSDMCFreeSpace() const { 55u64 SDMCFactory::GetSDMCFreeSpace() const {
47 return GetSDMCTotalSpace() - dir->GetSize(); 56 return GetSDMCTotalSpace() - sd_dir->GetSize();
48} 57}
49 58
50u64 SDMCFactory::GetSDMCTotalSpace() const { 59u64 SDMCFactory::GetSDMCTotalSpace() const {
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index 2bb92ba93..3a3d11f3a 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -16,11 +16,12 @@ class PlaceholderCache;
16/// File system interface to the SDCard archive 16/// File system interface to the SDCard archive
17class SDMCFactory { 17class SDMCFactory {
18public: 18public:
19 explicit SDMCFactory(VirtualDir dir); 19 explicit SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_);
20 ~SDMCFactory(); 20 ~SDMCFactory();
21 21
22 ResultVal<VirtualDir> Open() const; 22 ResultVal<VirtualDir> Open() const;
23 23
24 VirtualDir GetSDMCModificationLoadRoot(u64 title_id) const;
24 VirtualDir GetSDMCContentDirectory() const; 25 VirtualDir GetSDMCContentDirectory() const;
25 26
26 RegisteredCache* GetSDMCContents() const; 27 RegisteredCache* GetSDMCContents() const;
@@ -32,7 +33,8 @@ public:
32 u64 GetSDMCTotalSpace() const; 33 u64 GetSDMCTotalSpace() const;
33 34
34private: 35private:
35 VirtualDir dir; 36 VirtualDir sd_dir;
37 VirtualDir sd_mod_dir;
36 38
37 std::unique_ptr<RegisteredCache> contents; 39 std::unique_ptr<RegisteredCache> contents;
38 std::unique_ptr<PlaceholderCache> placeholder; 40 std::unique_ptr<PlaceholderCache> placeholder;
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.cpp b/src/core/file_sys/vfs.cpp
index 215e1cb1a..368419eca 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -6,7 +6,6 @@
6#include <numeric> 6#include <numeric>
7#include <string> 7#include <string>
8#include "common/fs/path_util.h" 8#include "common/fs/path_util.h"
9#include "common/logging/backend.h"
10#include "core/file_sys/mode.h" 9#include "core/file_sys/mode.h"
11#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
12 11
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp
index cd162c0c3..00e256779 100644
--- a/src/core/file_sys/vfs_libzip.cpp
+++ b/src/core/file_sys/vfs_libzip.cpp
@@ -14,7 +14,6 @@
14#endif 14#endif
15 15
16#include "common/fs/path_util.h" 16#include "common/fs/path_util.h"
17#include "common/logging/backend.h"
18#include "core/file_sys/vfs.h" 17#include "core/file_sys/vfs.h"
19#include "core/file_sys/vfs_libzip.h" 18#include "core/file_sys/vfs_libzip.h"
20#include "core/file_sys/vfs_vector.h" 19#include "core/file_sys/vfs_vector.h"
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 d9fdc2dca..dc15cf58b 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -19,7 +19,6 @@
19#include "common/fs/fs.h" 19#include "common/fs/fs.h"
20#include "common/fs/path_util.h" 20#include "common/fs/path_util.h"
21#include "common/hex_util.h" 21#include "common/hex_util.h"
22#include "common/logging/backend.h"
23#include "common/logging/log.h" 22#include "common/logging/log.h"
24#include "common/settings.h" 23#include "common/settings.h"
25#include "core/core.h" 24#include "core/core.h"
@@ -314,7 +313,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
314 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 313 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
315 314
316 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 315 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
317 void(Common::FS::RemoveFile(zip_path)); 316 Common::FS::RemoveFile(zip_path);
318 } 317 }
319 318
320 HandleDownloadDisplayResult(applet_manager, res); 319 HandleDownloadDisplayResult(applet_manager, res);
@@ -446,7 +445,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
446 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); 445 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
447 446
448 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { 447 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
449 void(Common::FS::RemoveFile(bin_file_path)); 448 Common::FS::RemoveFile(bin_file_path);
450 } 449 }
451 450
452 HandleDownloadDisplayResult(applet_manager, res); 451 HandleDownloadDisplayResult(applet_manager, res);
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 3c16fe6c7..4a9b13e45 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -703,6 +703,16 @@ FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id)
703 return bis_factory->GetModificationLoadRoot(title_id); 703 return bis_factory->GetModificationLoadRoot(title_id);
704} 704}
705 705
706FileSys::VirtualDir FileSystemController::GetSDMCModificationLoadRoot(u64 title_id) const {
707 LOG_TRACE(Service_FS, "Opening SDMC mod load root for tid={:016X}", title_id);
708
709 if (sdmc_factory == nullptr) {
710 return nullptr;
711 }
712
713 return sdmc_factory->GetSDMCModificationLoadRoot(title_id);
714}
715
706FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const { 716FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const {
707 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); 717 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
708 718
@@ -733,20 +743,23 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
733 } 743 }
734 744
735 using YuzuPath = Common::FS::YuzuPath; 745 using YuzuPath = Common::FS::YuzuPath;
746 const auto sdmc_dir_path = Common::FS::GetYuzuPath(YuzuPath::SDMCDir);
747 const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents";
736 const auto rw_mode = FileSys::Mode::ReadWrite; 748 const auto rw_mode = FileSys::Mode::ReadWrite;
737 749
738 auto nand_directory = 750 auto nand_directory =
739 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); 751 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode);
740 auto sd_directory = 752 auto sd_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_dir_path), rw_mode);
741 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::SDMCDir), rw_mode);
742 auto load_directory = 753 auto load_directory =
743 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read); 754 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read);
755 auto sd_load_directory =
756 vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path), FileSys::Mode::Read);
744 auto dump_directory = 757 auto dump_directory =
745 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode); 758 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode);
746 759
747 if (bis_factory == nullptr) { 760 if (bis_factory == nullptr) {
748 bis_factory = 761 bis_factory = std::make_unique<FileSys::BISFactory>(
749 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); 762 nand_directory, std::move(load_directory), std::move(dump_directory));
750 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND, 763 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND,
751 bis_factory->GetSystemNANDContents()); 764 bis_factory->GetSystemNANDContents());
752 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND, 765 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND,
@@ -759,7 +772,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
759 } 772 }
760 773
761 if (sdmc_factory == nullptr) { 774 if (sdmc_factory == nullptr) {
762 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 775 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory),
776 std::move(sd_load_directory));
763 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC, 777 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
764 sdmc_factory->GetSDMCContents()); 778 sdmc_factory->GetSDMCContents());
765 } 779 }
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index b6b1b9220..d387af3cb 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -115,6 +115,7 @@ public:
115 FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const; 115 FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const;
116 FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const; 116 FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const;
117 117
118 FileSys::VirtualDir GetSDMCModificationLoadRoot(u64 title_id) const;
118 FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; 119 FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
119 FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; 120 FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
120 121
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 3af9881c2..db4d44c12 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -13,6 +13,7 @@
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/hex_util.h" 14#include "common/hex_util.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/settings.h"
16#include "common/string_util.h" 17#include "common/string_util.h"
17#include "core/core.h" 18#include "core/core.h"
18#include "core/file_sys/directory.h" 19#include "core/file_sys/directory.h"
@@ -785,6 +786,10 @@ FSP_SRV::FSP_SRV(Core::System& system_)
785 }; 786 };
786 // clang-format on 787 // clang-format on
787 RegisterHandlers(functions); 788 RegisterHandlers(functions);
789
790 if (Settings::values.enable_fs_access_log) {
791 access_log_mode = AccessLogMode::SdCard;
792 }
788} 793}
789 794
790FSP_SRV::~FSP_SRV() = default; 795FSP_SRV::~FSP_SRV() = default;
@@ -1041,9 +1046,9 @@ void FSP_SRV::DisableAutoSaveDataCreation(Kernel::HLERequestContext& ctx) {
1041 1046
1042void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 1047void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
1043 IPC::RequestParser rp{ctx}; 1048 IPC::RequestParser rp{ctx};
1044 log_mode = rp.PopEnum<LogMode>(); 1049 access_log_mode = rp.PopEnum<AccessLogMode>();
1045 1050
1046 LOG_DEBUG(Service_FS, "called, log_mode={:08X}", log_mode); 1051 LOG_DEBUG(Service_FS, "called, access_log_mode={}", access_log_mode);
1047 1052
1048 IPC::ResponseBuilder rb{ctx, 2}; 1053 IPC::ResponseBuilder rb{ctx, 2};
1049 rb.Push(ResultSuccess); 1054 rb.Push(ResultSuccess);
@@ -1054,7 +1059,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
1054 1059
1055 IPC::ResponseBuilder rb{ctx, 3}; 1060 IPC::ResponseBuilder rb{ctx, 3};
1056 rb.Push(ResultSuccess); 1061 rb.Push(ResultSuccess);
1057 rb.PushEnum(log_mode); 1062 rb.PushEnum(access_log_mode);
1058} 1063}
1059 1064
1060void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { 1065void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
@@ -1062,9 +1067,9 @@ void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
1062 auto log = Common::StringFromFixedZeroTerminatedBuffer( 1067 auto log = Common::StringFromFixedZeroTerminatedBuffer(
1063 reinterpret_cast<const char*>(raw.data()), raw.size()); 1068 reinterpret_cast<const char*>(raw.data()), raw.size());
1064 1069
1065 LOG_DEBUG(Service_FS, "called, log='{}'", log); 1070 LOG_DEBUG(Service_FS, "called");
1066 1071
1067 reporter.SaveFilesystemAccessReport(log_mode, std::move(log)); 1072 reporter.SaveFSAccessLog(log);
1068 1073
1069 IPC::ResponseBuilder rb{ctx, 2}; 1074 IPC::ResponseBuilder rb{ctx, 2};
1070 rb.Push(ResultSuccess); 1075 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index ff7455a20..556708284 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -24,11 +24,10 @@ enum class AccessLogVersion : u32 {
24 Latest = V7_0_0, 24 Latest = V7_0_0,
25}; 25};
26 26
27enum class LogMode : u32 { 27enum class AccessLogMode : u32 {
28 Off, 28 None,
29 Log, 29 Log,
30 RedirectToSdCard, 30 SdCard,
31 LogToSdCard = Log | RedirectToSdCard,
32}; 31};
33 32
34class FSP_SRV final : public ServiceFramework<FSP_SRV> { 33class FSP_SRV final : public ServiceFramework<FSP_SRV> {
@@ -59,13 +58,12 @@ private:
59 58
60 FileSystemController& fsc; 59 FileSystemController& fsc;
61 const FileSys::ContentProvider& content_provider; 60 const FileSys::ContentProvider& content_provider;
61 const Core::Reporter& reporter;
62 62
63 FileSys::VirtualFile romfs; 63 FileSys::VirtualFile romfs;
64 u64 current_process_id = 0; 64 u64 current_process_id = 0;
65 u32 access_log_program_index = 0; 65 u32 access_log_program_index = 0;
66 LogMode log_mode = LogMode::LogToSdCard; 66 AccessLogMode access_log_mode = AccessLogMode::None;
67
68 const Core::Reporter& reporter;
69}; 67};
70 68
71} // namespace Service::FileSystem 69} // namespace Service::FileSystem
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/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index fa6213d3c..d68b023d0 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -236,7 +236,7 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} {
236 {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"}, 236 {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"},
237 {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, 237 {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
238 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, 238 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
239 {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"}, 239 {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
240 {91, &Hid::ActivateGesture, "ActivateGesture"}, 240 {91, &Hid::ActivateGesture, "ActivateGesture"},
241 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, 241 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
242 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, 242 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -710,6 +710,27 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
710 .IsSixAxisSensorAtRest()); 710 .IsSixAxisSensorAtRest());
711} 711}
712 712
713void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
714 IPC::RequestParser rp{ctx};
715 struct Parameters {
716 Controller_NPad::DeviceHandle sixaxis_handle;
717 INSERT_PADDING_WORDS_NOINIT(1);
718 u64 applet_resource_user_id;
719 };
720
721 const auto parameters{rp.PopRaw<Parameters>()};
722
723 LOG_WARNING(
724 Service_HID,
725 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
726 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
727 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
728
729 IPC::ResponseBuilder rb{ctx, 3};
730 rb.Push(ResultSuccess);
731 rb.Push(false);
732}
733
713void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { 734void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
714 IPC::RequestParser rp{ctx}; 735 IPC::RequestParser rp{ctx};
715 struct Parameters { 736 struct Parameters {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index aa3307955..83fc2ea1d 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -100,6 +100,7 @@ private:
100 void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); 100 void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
101 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); 101 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
102 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); 102 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
103 void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx);
103 void ActivateGesture(Kernel::HLERequestContext& ctx); 104 void ActivateGesture(Kernel::HLERequestContext& ctx);
104 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 105 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
105 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 106 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 311e4fb2d..794504314 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -51,6 +51,24 @@ struct hash<Service::LM::LogPacketHeaderEntry> {
51} // namespace std 51} // namespace std
52 52
53namespace Service::LM { 53namespace Service::LM {
54namespace {
55std::string_view NameOf(LogSeverity severity) {
56 switch (severity) {
57 case LogSeverity::Trace:
58 return "TRACE";
59 case LogSeverity::Info:
60 return "INFO";
61 case LogSeverity::Warning:
62 return "WARNING";
63 case LogSeverity::Error:
64 return "ERROR";
65 case LogSeverity::Fatal:
66 return "FATAL";
67 default:
68 return "UNKNOWN";
69 }
70}
71} // Anonymous namespace
54 72
55enum class LogDestination : u32 { 73enum class LogDestination : u32 {
56 TargetManager = 1 << 0, 74 TargetManager = 1 << 0,
@@ -262,33 +280,8 @@ private:
262 if (text_log) { 280 if (text_log) {
263 output_log += fmt::format("Log Text: {}\n", *text_log); 281 output_log += fmt::format("Log Text: {}\n", *text_log);
264 } 282 }
265 283 LOG_DEBUG(Service_LM, "LogManager {} ({}):\n{}", NameOf(entry.severity),
266 switch (entry.severity) { 284 DestinationToString(destination), output_log);
267 case LogSeverity::Trace:
268 LOG_DEBUG(Service_LM, "LogManager TRACE ({}):\n{}", DestinationToString(destination),
269 output_log);
270 break;
271 case LogSeverity::Info:
272 LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination),
273 output_log);
274 break;
275 case LogSeverity::Warning:
276 LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}",
277 DestinationToString(destination), output_log);
278 break;
279 case LogSeverity::Error:
280 LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination),
281 output_log);
282 break;
283 case LogSeverity::Fatal:
284 LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination),
285 output_log);
286 break;
287 default:
288 LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}",
289 DestinationToString(destination), output_log);
290 break;
291 }
292 } 285 }
293 286
294 static std::string DestinationToString(LogDestination destination) { 287 static std::string DestinationToString(LogDestination destination) {
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/core/reporter.cpp b/src/core/reporter.cpp
index ec2a16e62..82b0f535a 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -195,7 +195,9 @@ json GetHLERequestContextData(Kernel::HLERequestContext& ctx, Core::Memory::Memo
195 195
196namespace Core { 196namespace Core {
197 197
198Reporter::Reporter(System& system_) : system(system_) {} 198Reporter::Reporter(System& system_) : system(system_) {
199 ClearFSAccessLog();
200}
199 201
200Reporter::~Reporter() = default; 202Reporter::~Reporter() = default;
201 203
@@ -362,22 +364,12 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
362 SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); 364 SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp));
363} 365}
364 366
365void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, 367void Reporter::SaveFSAccessLog(std::string_view log_message) const {
366 std::string log_message) const { 368 const auto access_log_path =
367 if (!IsReportingEnabled()) 369 Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "FsAccessLog.txt";
368 return;
369
370 const auto timestamp = GetTimestamp();
371 const auto title_id = system.CurrentProcess()->GetTitleID();
372 json out;
373 370
374 out["yuzu_version"] = GetYuzuVersionData(); 371 void(Common::FS::AppendStringToFile(access_log_path, Common::FS::FileType::TextFile,
375 out["report_common"] = GetReportCommonData(title_id, ResultSuccess, timestamp); 372 log_message));
376
377 out["log_mode"] = fmt::format("{:08X}", static_cast<u32>(log_mode));
378 out["log_message"] = std::move(log_message);
379
380 SaveToFile(std::move(out), GetPath("filesystem_access_report", title_id, timestamp));
381} 373}
382 374
383void Reporter::SaveUserReport() const { 375void Reporter::SaveUserReport() const {
@@ -392,6 +384,18 @@ void Reporter::SaveUserReport() const {
392 GetPath("user_report", title_id, timestamp)); 384 GetPath("user_report", title_id, timestamp));
393} 385}
394 386
387void Reporter::ClearFSAccessLog() const {
388 const auto access_log_path =
389 Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "FsAccessLog.txt";
390
391 Common::FS::IOFile access_log_file{access_log_path, Common::FS::FileAccessMode::Write,
392 Common::FS::FileType::TextFile};
393
394 if (!access_log_file.IsOpen()) {
395 LOG_ERROR(Common_Filesystem, "Failed to clear the filesystem access log.");
396 }
397}
398
395bool Reporter::IsReportingEnabled() const { 399bool Reporter::IsReportingEnabled() const {
396 return Settings::values.reporting_services; 400 return Settings::values.reporting_services;
397} 401}
diff --git a/src/core/reporter.h b/src/core/reporter.h
index 6fb6ebffa..6e9edeea3 100644
--- a/src/core/reporter.h
+++ b/src/core/reporter.h
@@ -16,10 +16,6 @@ namespace Kernel {
16class HLERequestContext; 16class HLERequestContext;
17} // namespace Kernel 17} // namespace Kernel
18 18
19namespace Service::FileSystem {
20enum class LogMode : u32;
21}
22
23namespace Service::LM { 19namespace Service::LM {
24struct LogMessage; 20struct LogMessage;
25} // namespace Service::LM 21} // namespace Service::LM
@@ -69,14 +65,15 @@ public:
69 std::optional<std::string> custom_text_main = {}, 65 std::optional<std::string> custom_text_main = {},
70 std::optional<std::string> custom_text_detail = {}) const; 66 std::optional<std::string> custom_text_detail = {}) const;
71 67
72 void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, 68 void SaveFSAccessLog(std::string_view log_message) const;
73 std::string log_message) const;
74 69
75 // Can be used anywhere to generate a backtrace and general info report at any point during 70 // Can be used anywhere to generate a backtrace and general info report at any point during
76 // execution. Not intended to be used for anything other than debugging or testing. 71 // execution. Not intended to be used for anything other than debugging or testing.
77 void SaveUserReport() const; 72 void SaveUserReport() const;
78 73
79private: 74private:
75 void ClearFSAccessLog() const;
76
80 bool IsReportingEnabled() const; 77 bool IsReportingEnabled() const;
81 78
82 System& system; 79 System& system;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index ad1a9ffb4..d4c23ced2 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -230,6 +230,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
230 Settings::values.use_asynchronous_gpu_emulation.GetValue()); 230 Settings::values.use_asynchronous_gpu_emulation.GetValue());
231 AddField(field_type, "Renderer_UseNvdecEmulation", 231 AddField(field_type, "Renderer_UseNvdecEmulation",
232 Settings::values.use_nvdec_emulation.GetValue()); 232 Settings::values.use_nvdec_emulation.GetValue());
233 AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
233 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); 234 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
234 AddField(field_type, "Renderer_UseAssemblyShaders", 235 AddField(field_type, "Renderer_UseAssemblyShaders",
235 Settings::values.use_assembly_shaders.GetValue()); 236 Settings::values.use_assembly_shaders.GetValue());
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 47190c464..e31eb30c0 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -237,6 +237,7 @@ add_library(video_core STATIC
237 texture_cache/util.cpp 237 texture_cache/util.cpp
238 texture_cache/util.h 238 texture_cache/util.h
239 textures/astc.h 239 textures/astc.h
240 textures/astc.cpp
240 textures/decoders.cpp 241 textures/decoders.cpp
241 textures/decoders.h 242 textures/decoders.h
242 textures/texture.cpp 243 textures/texture.cpp
@@ -292,6 +293,7 @@ endif()
292if (MSVC) 293if (MSVC)
293 target_compile_options(video_core PRIVATE 294 target_compile_options(video_core PRIVATE
294 /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
295 /we4456 # Declaration of 'identifier' hides previous local declaration 297 /we4456 # Declaration of 'identifier' hides previous local declaration
296 /we4457 # Declaration of 'identifier' hides function parameter 298 /we4457 # Declaration of 'identifier' hides function parameter
297 /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 703e34587..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}
@@ -763,7 +754,7 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode) {
763 case 1: { 754 case 1: {
764 READ_UINT_VALUES(2) 755 READ_UINT_VALUES(2)
765 uint L0 = (v[0] >> 2) | (v[1] & 0xC0); 756 uint L0 = (v[0] >> 2) | (v[1] & 0xC0);
766 uint L1 = max(L0 + (v[1] & 0x3F), 0xFFU); 757 uint L1 = min(L0 + (v[1] & 0x3F), 0xFFU);
767 ep1 = uvec4(0xFF, L0, L0, L0); 758 ep1 = uvec4(0xFF, L0, L0, L0);
768 ep2 = uvec4(0xFF, L1, L1, L1); 759 ep2 = uvec4(0xFF, L1, L1, L1);
769 break; 760 break;
@@ -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_base.h b/src/video_core/renderer_base.h
index 320ee8d30..63d8ad42a 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -42,6 +42,8 @@ public:
42 42
43 [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; 43 [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0;
44 44
45 [[nodiscard]] virtual std::string GetDeviceVendor() const = 0;
46
45 // Getter/setter functions: 47 // Getter/setter functions:
46 // ------------------------ 48 // ------------------------
47 49
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_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 3f4532ca7..3b00614e7 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -202,13 +202,13 @@ Device::Device() {
202 LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); 202 LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available");
203 throw std::runtime_error{"Insufficient version"}; 203 throw std::runtime_error{"Insufficient version"};
204 } 204 }
205 const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); 205 vendor_name = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
206 const std::string_view version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); 206 const std::string_view version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
207 const std::vector extensions = GetExtensions(); 207 const std::vector extensions = GetExtensions();
208 208
209 const bool is_nvidia = vendor == "NVIDIA Corporation"; 209 const bool is_nvidia = vendor_name == "NVIDIA Corporation";
210 const bool is_amd = vendor == "ATI Technologies Inc."; 210 const bool is_amd = vendor_name == "ATI Technologies Inc.";
211 const bool is_intel = vendor == "Intel"; 211 const bool is_intel = vendor_name == "Intel";
212 212
213#ifdef __unix__ 213#ifdef __unix__
214 const bool is_linux = true; 214 const bool is_linux = true;
@@ -275,6 +275,56 @@ Device::Device() {
275 } 275 }
276} 276}
277 277
278std::string Device::GetVendorName() const {
279 if (vendor_name == "NVIDIA Corporation") {
280 return "NVIDIA";
281 }
282 if (vendor_name == "ATI Technologies Inc.") {
283 return "AMD";
284 }
285 if (vendor_name == "Intel") {
286 // For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris.
287 // Simply return `INTEL` for those as well as the Windows driver.
288 return "INTEL";
289 }
290 if (vendor_name == "Intel Open Source Technology Center") {
291 return "I965";
292 }
293 if (vendor_name == "Mesa Project") {
294 return "I915";
295 }
296 if (vendor_name == "Mesa/X.org") {
297 // This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return
298 // MESA instead of one of those driver names.
299 return "MESA";
300 }
301 if (vendor_name == "AMD") {
302 return "RADEONSI";
303 }
304 if (vendor_name == "nouveau") {
305 return "NOUVEAU";
306 }
307 if (vendor_name == "X.Org") {
308 return "R600";
309 }
310 if (vendor_name == "Collabora Ltd") {
311 return "ZINK";
312 }
313 if (vendor_name == "Intel Corporation") {
314 return "OPENSWR";
315 }
316 if (vendor_name == "Microsoft Corporation") {
317 return "D3D12";
318 }
319 if (vendor_name == "NVIDIA") {
320 // Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default
321 // strategy would have returned `NVIDIA` here for this driver, the same result as the
322 // proprietary driver.
323 return "TEGRA";
324 }
325 return vendor_name;
326}
327
278Device::Device(std::nullptr_t) { 328Device::Device(std::nullptr_t) {
279 max_uniform_buffers.fill(std::numeric_limits<u32>::max()); 329 max_uniform_buffers.fill(std::numeric_limits<u32>::max());
280 uniform_buffer_alignment = 4; 330 uniform_buffer_alignment = 4;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index f24bd0c7b..2c2b13767 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -22,6 +22,8 @@ public:
22 explicit Device(); 22 explicit Device();
23 explicit Device(std::nullptr_t); 23 explicit Device(std::nullptr_t);
24 24
25 [[nodiscard]] std::string GetVendorName() const;
26
25 u32 GetMaxUniformBuffers(Tegra::Engines::ShaderType shader_type) const noexcept { 27 u32 GetMaxUniformBuffers(Tegra::Engines::ShaderType shader_type) const noexcept {
26 return max_uniform_buffers[static_cast<std::size_t>(shader_type)]; 28 return max_uniform_buffers[static_cast<std::size_t>(shader_type)];
27 } 29 }
@@ -130,6 +132,7 @@ private:
130 static bool TestVariableAoffi(); 132 static bool TestVariableAoffi();
131 static bool TestPreciseBug(); 133 static bool TestPreciseBug();
132 134
135 std::string vendor_name;
133 std::array<u32, Tegra::Engines::MaxShaderTypes> max_uniform_buffers{}; 136 std::array<u32, Tegra::Engines::MaxShaderTypes> max_uniform_buffers{};
134 std::array<BaseBindings, Tegra::Engines::MaxShaderTypes> base_bindings{}; 137 std::array<BaseBindings, Tegra::Engines::MaxShaderTypes> base_bindings{};
135 size_t uniform_buffer_alignment{}; 138 size_t uniform_buffer_alignment{};
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 ffe9edc1b..23948feed 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -9,6 +9,8 @@
9 9
10#include <glad/glad.h> 10#include <glad/glad.h>
11 11
12#include "common/settings.h"
13
12#include "video_core/renderer_opengl/gl_device.h" 14#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_shader_manager.h" 15#include "video_core/renderer_opengl/gl_shader_manager.h"
14#include "video_core/renderer_opengl/gl_state_tracker.h" 16#include "video_core/renderer_opengl/gl_state_tracker.h"
@@ -307,7 +309,9 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
307 309
308[[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime, 310[[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime,
309 const VideoCommon::ImageInfo& info) { 311 const VideoCommon::ImageInfo& info) {
310 return !runtime.HasNativeASTC() && IsPixelFormatASTC(info.format); 312 if (IsPixelFormatASTC(info.format)) {
313 return !runtime.HasNativeASTC() && Settings::values.accelerate_astc.GetValue();
314 }
311 // Disable other accelerated uploads for now as they don't implement swizzled uploads 315 // Disable other accelerated uploads for now as they don't implement swizzled uploads
312 return false; 316 return false;
313 switch (info.type) { 317 switch (info.type) {
@@ -733,6 +737,8 @@ Image::Image(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info_,
733 } 737 }
734} 738}
735 739
740Image::~Image() = default;
741
736void Image::UploadMemory(const ImageBufferMap& map, 742void Image::UploadMemory(const ImageBufferMap& map,
737 std::span<const VideoCommon::BufferImageCopy> copies) { 743 std::span<const VideoCommon::BufferImageCopy> copies) {
738 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/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index cc19a110f..0b66f8332 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -70,6 +70,10 @@ public:
70 return &rasterizer; 70 return &rasterizer;
71 } 71 }
72 72
73 [[nodiscard]] std::string GetDeviceVendor() const override {
74 return device.GetVendorName();
75 }
76
73private: 77private:
74 /// Initializes the OpenGL state and creates persistent objects. 78 /// Initializes the OpenGL state and creates persistent objects.
75 void InitOpenGLObjects(); 79 void InitOpenGLObjects();
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/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 72071316c..d7d17e110 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -47,6 +47,10 @@ public:
47 return &rasterizer; 47 return &rasterizer;
48 } 48 }
49 49
50 [[nodiscard]] std::string GetDeviceVendor() const override {
51 return device.GetDriverName();
52 }
53
50private: 54private:
51 void Report() const; 55 void Report() const;
52 56
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 bdd0ce8bc..a2ab4d1ee 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/bit_cast.h" 10#include "common/bit_cast.h"
11#include "common/settings.h"
11 12
12#include "video_core/engines/fermi_2d.h" 13#include "video_core/engines/fermi_2d.h"
13#include "video_core/renderer_vulkan/blit_image.h" 14#include "video_core/renderer_vulkan/blit_image.h"
@@ -817,6 +818,10 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
817 }); 818 });
818} 819}
819 820
821u64 TextureCacheRuntime::GetDeviceLocalMemory() const {
822 return device.GetDeviceLocalMemory();
823}
824
820Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_addr_, 825Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_addr_,
821 VAddr cpu_addr_) 826 VAddr cpu_addr_)
822 : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime.scheduler}, 827 : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime.scheduler},
@@ -828,7 +833,11 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_
828 commit = runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal); 833 commit = runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
829 } 834 }
830 if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) { 835 if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) {
831 flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; 836 if (Settings::values.accelerate_astc.GetValue()) {
837 flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
838 } else {
839 flags |= VideoCommon::ImageFlagBits::Converted;
840 }
832 } 841 }
833 if (runtime.device.HasDebuggingToolAttached()) { 842 if (runtime.device.HasDebuggingToolAttached()) {
834 if (image) { 843 if (image) {
@@ -871,6 +880,8 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_
871 } 880 }
872} 881}
873 882
883Image::~Image() = default;
884
874void Image::UploadMemory(const StagingBufferRef& map, std::span<const BufferImageCopy> copies) { 885void Image::UploadMemory(const StagingBufferRef& map, std::span<const BufferImageCopy> copies) {
875 // TODO: Move this to another API 886 // TODO: Move this to another API
876 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 906604a39..4efe042b6 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -47,6 +47,7 @@
47#include "video_core/texture_cache/formatter.h" 47#include "video_core/texture_cache/formatter.h"
48#include "video_core/texture_cache/samples_helper.h" 48#include "video_core/texture_cache/samples_helper.h"
49#include "video_core/texture_cache/util.h" 49#include "video_core/texture_cache/util.h"
50#include "video_core/textures/astc.h"
50#include "video_core/textures/decoders.h" 51#include "video_core/textures/decoders.h"
51 52
52namespace VideoCommon { 53namespace VideoCommon {
@@ -580,6 +581,8 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
580 581
581 for (s32 layer = 0; layer < info.resources.layers; ++layer) { 582 for (s32 layer = 0; layer < info.resources.layers; ++layer) {
582 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
583 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,
584 num_tiles.depth, block.height, block.depth); 587 num_tiles.depth, block.height, block.depth);
585 588
@@ -884,8 +887,16 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
884 ASSERT(copy.image_extent == mip_size); 887 ASSERT(copy.image_extent == mip_size);
885 ASSERT(copy.buffer_row_length == Common::AlignUp(mip_size.width, tile_size.width)); 888 ASSERT(copy.buffer_row_length == Common::AlignUp(mip_size.width, tile_size.width));
886 ASSERT(copy.buffer_image_height == Common::AlignUp(mip_size.height, tile_size.height)); 889 ASSERT(copy.buffer_image_height == Common::AlignUp(mip_size.height, tile_size.height));
887 DecompressBC4(input.subspan(copy.buffer_offset), copy.image_extent, 890 if (IsPixelFormatASTC(info.format)) {
888 output.subspan(output_offset)); 891 ASSERT(copy.image_extent.depth == 1);
892 Tegra::Texture::ASTC::Decompress(input.subspan(copy.buffer_offset),
893 copy.image_extent.width, copy.image_extent.height,
894 copy.image_subresource.num_layers, tile_size.width,
895 tile_size.height, output.subspan(output_offset));
896 } else {
897 DecompressBC4(input.subspan(copy.buffer_offset), copy.image_extent,
898 output.subspan(output_offset));
899 }
889 copy.buffer_offset = output_offset; 900 copy.buffer_offset = output_offset;
890 copy.buffer_row_length = mip_size.width; 901 copy.buffer_row_length = mip_size.width;
891 copy.buffer_image_height = mip_size.height; 902 copy.buffer_image_height = mip_size.height;
@@ -1087,7 +1098,15 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const
1087 return std::nullopt; 1098 return std::nullopt;
1088 } 1099 }
1089 const ImageInfo& existing = image.info; 1100 const ImageInfo& existing = image.info;
1090 if (False(options & RelaxedOptions::Format)) { 1101 if (True(options & RelaxedOptions::Format)) {
1102 // Format checking is relaxed, but we still have to check for matching bytes per block.
1103 // This avoids creating a view for blits on UE4 titles where formats with different bytes
1104 // per block are aliased.
1105 if (BytesPerBlock(existing.format) != BytesPerBlock(candidate.format)) {
1106 return std::nullopt;
1107 }
1108 } else {
1109 // Format comaptibility is not relaxed, ensure we are creating a view on a compatible format
1091 if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) { 1110 if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) {
1092 return std::nullopt; 1111 return std::nullopt;
1093 } 1112 }
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
new file mode 100644
index 000000000..7b756ba41
--- /dev/null
+++ b/src/video_core/textures/astc.cpp
@@ -0,0 +1,1579 @@
1// Copyright 2016 The University of North Carolina at Chapel Hill
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// Please send all BUG REPORTS to <pavel@cs.unc.edu>.
16// <http://gamma.cs.unc.edu/FasTC/>
17
18#include <algorithm>
19#include <cassert>
20#include <cstring>
21#include <span>
22#include <vector>
23
24#include <boost/container/static_vector.hpp>
25
26#include "common/common_types.h"
27#include "video_core/textures/astc.h"
28
29class InputBitStream {
30public:
31 constexpr explicit InputBitStream(std::span<const u8> data, size_t start_offset = 0)
32 : cur_byte{data.data()}, total_bits{data.size()}, next_bit{start_offset % 8} {}
33
34 constexpr size_t GetBitsRead() const {
35 return bits_read;
36 }
37
38 constexpr bool ReadBit() {
39 if (bits_read >= total_bits * 8) {
40 return 0;
41 }
42 const bool bit = ((*cur_byte >> next_bit) & 1) != 0;
43 ++next_bit;
44 while (next_bit >= 8) {
45 next_bit -= 8;
46 ++cur_byte;
47 }
48 ++bits_read;
49 return bit;
50 }
51
52 constexpr u32 ReadBits(std::size_t nBits) {
53 u32 ret = 0;
54 for (std::size_t i = 0; i < nBits; ++i) {
55 ret |= (ReadBit() & 1) << i;
56 }
57 return ret;
58 }
59
60 template <std::size_t nBits>
61 constexpr u32 ReadBits() {
62 u32 ret = 0;
63 for (std::size_t i = 0; i < nBits; ++i) {
64 ret |= (ReadBit() & 1) << i;
65 }
66 return ret;
67 }
68
69private:
70 const u8* cur_byte;
71 size_t total_bits = 0;
72 size_t next_bit = 0;
73 size_t bits_read = 0;
74};
75
76class OutputBitStream {
77public:
78 constexpr explicit OutputBitStream(u8* ptr, std::size_t bits = 0, std::size_t start_offset = 0)
79 : cur_byte{ptr}, num_bits{bits}, next_bit{start_offset % 8} {}
80
81 constexpr std::size_t GetBitsWritten() const {
82 return bits_written;
83 }
84
85 constexpr void WriteBitsR(u32 val, u32 nBits) {
86 for (u32 i = 0; i < nBits; i++) {
87 WriteBit((val >> (nBits - i - 1)) & 1);
88 }
89 }
90
91 constexpr void WriteBits(u32 val, u32 nBits) {
92 for (u32 i = 0; i < nBits; i++) {
93 WriteBit((val >> i) & 1);
94 }
95 }
96
97private:
98 constexpr void WriteBit(bool b) {
99 if (bits_written >= num_bits) {
100 return;
101 }
102
103 const u32 mask = 1 << next_bit++;
104
105 // clear the bit
106 *cur_byte &= static_cast<u8>(~mask);
107
108 // Write the bit, if necessary
109 if (b)
110 *cur_byte |= static_cast<u8>(mask);
111
112 // Next byte?
113 if (next_bit >= 8) {
114 cur_byte += 1;
115 next_bit = 0;
116 }
117 }
118
119 u8* cur_byte;
120 std::size_t num_bits;
121 std::size_t bits_written = 0;
122 std::size_t next_bit = 0;
123};
124
125template <typename IntType>
126class Bits {
127public:
128 explicit Bits(const IntType& v) : m_Bits(v) {}
129
130 Bits(const Bits&) = delete;
131 Bits& operator=(const Bits&) = delete;
132
133 u8 operator[](u32 bitPos) const {
134 return static_cast<u8>((m_Bits >> bitPos) & 1);
135 }
136
137 IntType operator()(u32 start, u32 end) const {
138 if (start == end) {
139 return (*this)[start];
140 } else if (start > end) {
141 u32 t = start;
142 start = end;
143 end = t;
144 }
145
146 u64 mask = (1 << (end - start + 1)) - 1;
147 return (m_Bits >> start) & static_cast<IntType>(mask);
148 }
149
150private:
151 const IntType& m_Bits;
152};
153
154namespace Tegra::Texture::ASTC {
155using IntegerEncodedVector = boost::container::static_vector<
156 IntegerEncodedValue, 256,
157 boost::container::static_vector_options<
158 boost::container::inplace_alignment<alignof(IntegerEncodedValue)>,
159 boost::container::throw_on_overflow<false>>::type>;
160
161static void DecodeTritBlock(InputBitStream& bits, IntegerEncodedVector& result, u32 nBitsPerValue) {
162 // Implement the algorithm in section C.2.12
163 std::array<u32, 5> m;
164 std::array<u32, 5> t;
165 u32 T;
166
167 // Read the trit encoded block according to
168 // table C.2.14
169 m[0] = bits.ReadBits(nBitsPerValue);
170 T = bits.ReadBits<2>();
171 m[1] = bits.ReadBits(nBitsPerValue);
172 T |= bits.ReadBits<2>() << 2;
173 m[2] = bits.ReadBits(nBitsPerValue);
174 T |= bits.ReadBit() << 4;
175 m[3] = bits.ReadBits(nBitsPerValue);
176 T |= bits.ReadBits<2>() << 5;
177 m[4] = bits.ReadBits(nBitsPerValue);
178 T |= bits.ReadBit() << 7;
179
180 u32 C = 0;
181
182 Bits<u32> Tb(T);
183 if (Tb(2, 4) == 7) {
184 C = (Tb(5, 7) << 2) | Tb(0, 1);
185 t[4] = t[3] = 2;
186 } else {
187 C = Tb(0, 4);
188 if (Tb(5, 6) == 3) {
189 t[4] = 2;
190 t[3] = Tb[7];
191 } else {
192 t[4] = Tb[7];
193 t[3] = Tb(5, 6);
194 }
195 }
196
197 Bits<u32> Cb(C);
198 if (Cb(0, 1) == 3) {
199 t[2] = 2;
200 t[1] = Cb[4];
201 t[0] = (Cb[3] << 1) | (Cb[2] & ~Cb[3]);
202 } else if (Cb(2, 3) == 3) {
203 t[2] = 2;
204 t[1] = 2;
205 t[0] = Cb(0, 1);
206 } else {
207 t[2] = Cb[4];
208 t[1] = Cb(2, 3);
209 t[0] = (Cb[1] << 1) | (Cb[0] & ~Cb[1]);
210 }
211
212 for (std::size_t i = 0; i < 5; ++i) {
213 IntegerEncodedValue& val = result.emplace_back(IntegerEncoding::Trit, nBitsPerValue);
214 val.bit_value = m[i];
215 val.trit_value = t[i];
216 }
217}
218
219static void DecodeQuintBlock(InputBitStream& bits, IntegerEncodedVector& result,
220 u32 nBitsPerValue) {
221 // Implement the algorithm in section C.2.12
222 u32 m[3];
223 u32 q[3];
224 u32 Q;
225
226 // Read the trit encoded block according to
227 // table C.2.15
228 m[0] = bits.ReadBits(nBitsPerValue);
229 Q = bits.ReadBits<3>();
230 m[1] = bits.ReadBits(nBitsPerValue);
231 Q |= bits.ReadBits<2>() << 3;
232 m[2] = bits.ReadBits(nBitsPerValue);
233 Q |= bits.ReadBits<2>() << 5;
234
235 Bits<u32> Qb(Q);
236 if (Qb(1, 2) == 3 && Qb(5, 6) == 0) {
237 q[0] = q[1] = 4;
238 q[2] = (Qb[0] << 2) | ((Qb[4] & ~Qb[0]) << 1) | (Qb[3] & ~Qb[0]);
239 } else {
240 u32 C = 0;
241 if (Qb(1, 2) == 3) {
242 q[2] = 4;
243 C = (Qb(3, 4) << 3) | ((~Qb(5, 6) & 3) << 1) | Qb[0];
244 } else {
245 q[2] = Qb(5, 6);
246 C = Qb(0, 4);
247 }
248
249 Bits<u32> Cb(C);
250 if (Cb(0, 2) == 5) {
251 q[1] = 4;
252 q[0] = Cb(3, 4);
253 } else {
254 q[1] = Cb(3, 4);
255 q[0] = Cb(0, 2);
256 }
257 }
258
259 for (std::size_t i = 0; i < 3; ++i) {
260 IntegerEncodedValue& val = result.emplace_back(IntegerEncoding::Quint, nBitsPerValue);
261 val.bit_value = m[i];
262 val.quint_value = q[i];
263 }
264}
265
266// Fills result with the values that are encoded in the given
267// bitstream. We must know beforehand what the maximum possible
268// value is, and how many values we're decoding.
269static void DecodeIntegerSequence(IntegerEncodedVector& result, InputBitStream& bits, u32 maxRange,
270 u32 nValues) {
271 // Determine encoding parameters
272 IntegerEncodedValue val = ASTC_ENCODINGS_VALUES[maxRange];
273
274 // Start decoding
275 u32 nValsDecoded = 0;
276 while (nValsDecoded < nValues) {
277 switch (val.encoding) {
278 case IntegerEncoding::Quint:
279 DecodeQuintBlock(bits, result, val.num_bits);
280 nValsDecoded += 3;
281 break;
282
283 case IntegerEncoding::Trit:
284 DecodeTritBlock(bits, result, val.num_bits);
285 nValsDecoded += 5;
286 break;
287
288 case IntegerEncoding::JustBits:
289 val.bit_value = bits.ReadBits(val.num_bits);
290 result.push_back(val);
291 nValsDecoded++;
292 break;
293 }
294 }
295}
296
297struct TexelWeightParams {
298 u32 m_Width = 0;
299 u32 m_Height = 0;
300 bool m_bDualPlane = false;
301 u32 m_MaxWeight = 0;
302 bool m_bError = false;
303 bool m_bVoidExtentLDR = false;
304 bool m_bVoidExtentHDR = false;
305
306 u32 GetPackedBitSize() const {
307 // How many indices do we have?
308 u32 nIdxs = m_Height * m_Width;
309 if (m_bDualPlane) {
310 nIdxs *= 2;
311 }
312
313 return ASTC_ENCODINGS_VALUES[m_MaxWeight].GetBitLength(nIdxs);
314 }
315
316 u32 GetNumWeightValues() const {
317 u32 ret = m_Width * m_Height;
318 if (m_bDualPlane) {
319 ret *= 2;
320 }
321 return ret;
322 }
323};
324
325static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
326 TexelWeightParams params;
327
328 // Read the entire block mode all at once
329 u16 modeBits = static_cast<u16>(strm.ReadBits<11>());
330
331 // Does this match the void extent block mode?
332 if ((modeBits & 0x01FF) == 0x1FC) {
333 if (modeBits & 0x200) {
334 params.m_bVoidExtentHDR = true;
335 } else {
336 params.m_bVoidExtentLDR = true;
337 }
338
339 // Next two bits must be one.
340 if (!(modeBits & 0x400) || !strm.ReadBit()) {
341 params.m_bError = true;
342 }
343
344 return params;
345 }
346
347 // First check if the last four bits are zero
348 if ((modeBits & 0xF) == 0) {
349 params.m_bError = true;
350 return params;
351 }
352
353 // If the last two bits are zero, then if bits
354 // [6-8] are all ones, this is also reserved.
355 if ((modeBits & 0x3) == 0 && (modeBits & 0x1C0) == 0x1C0) {
356 params.m_bError = true;
357 return params;
358 }
359
360 // Otherwise, there is no error... Figure out the layout
361 // of the block mode. Layout is determined by a number
362 // between 0 and 9 corresponding to table C.2.8 of the
363 // ASTC spec.
364 u32 layout = 0;
365
366 if ((modeBits & 0x1) || (modeBits & 0x2)) {
367 // layout is in [0-4]
368 if (modeBits & 0x8) {
369 // layout is in [2-4]
370 if (modeBits & 0x4) {
371 // layout is in [3-4]
372 if (modeBits & 0x100) {
373 layout = 4;
374 } else {
375 layout = 3;
376 }
377 } else {
378 layout = 2;
379 }
380 } else {
381 // layout is in [0-1]
382 if (modeBits & 0x4) {
383 layout = 1;
384 } else {
385 layout = 0;
386 }
387 }
388 } else {
389 // layout is in [5-9]
390 if (modeBits & 0x100) {
391 // layout is in [7-9]
392 if (modeBits & 0x80) {
393 // layout is in [7-8]
394 assert((modeBits & 0x40) == 0U);
395 if (modeBits & 0x20) {
396 layout = 8;
397 } else {
398 layout = 7;
399 }
400 } else {
401 layout = 9;
402 }
403 } else {
404 // layout is in [5-6]
405 if (modeBits & 0x80) {
406 layout = 6;
407 } else {
408 layout = 5;
409 }
410 }
411 }
412
413 assert(layout < 10);
414
415 // Determine R
416 u32 R = !!(modeBits & 0x10);
417 if (layout < 5) {
418 R |= (modeBits & 0x3) << 1;
419 } else {
420 R |= (modeBits & 0xC) >> 1;
421 }
422 assert(2 <= R && R <= 7);
423
424 // Determine width & height
425 switch (layout) {
426 case 0: {
427 u32 A = (modeBits >> 5) & 0x3;
428 u32 B = (modeBits >> 7) & 0x3;
429 params.m_Width = B + 4;
430 params.m_Height = A + 2;
431 break;
432 }
433
434 case 1: {
435 u32 A = (modeBits >> 5) & 0x3;
436 u32 B = (modeBits >> 7) & 0x3;
437 params.m_Width = B + 8;
438 params.m_Height = A + 2;
439 break;
440 }
441
442 case 2: {
443 u32 A = (modeBits >> 5) & 0x3;
444 u32 B = (modeBits >> 7) & 0x3;
445 params.m_Width = A + 2;
446 params.m_Height = B + 8;
447 break;
448 }
449
450 case 3: {
451 u32 A = (modeBits >> 5) & 0x3;
452 u32 B = (modeBits >> 7) & 0x1;
453 params.m_Width = A + 2;
454 params.m_Height = B + 6;
455 break;
456 }
457
458 case 4: {
459 u32 A = (modeBits >> 5) & 0x3;
460 u32 B = (modeBits >> 7) & 0x1;
461 params.m_Width = B + 2;
462 params.m_Height = A + 2;
463 break;
464 }
465
466 case 5: {
467 u32 A = (modeBits >> 5) & 0x3;
468 params.m_Width = 12;
469 params.m_Height = A + 2;
470 break;
471 }
472
473 case 6: {
474 u32 A = (modeBits >> 5) & 0x3;
475 params.m_Width = A + 2;
476 params.m_Height = 12;
477 break;
478 }
479
480 case 7: {
481 params.m_Width = 6;
482 params.m_Height = 10;
483 break;
484 }
485
486 case 8: {
487 params.m_Width = 10;
488 params.m_Height = 6;
489 break;
490 }
491
492 case 9: {
493 u32 A = (modeBits >> 5) & 0x3;
494 u32 B = (modeBits >> 9) & 0x3;
495 params.m_Width = A + 6;
496 params.m_Height = B + 6;
497 break;
498 }
499
500 default:
501 assert(false && "Don't know this layout...");
502 params.m_bError = true;
503 break;
504 }
505
506 // Determine whether or not we're using dual planes
507 // and/or high precision layouts.
508 bool D = (layout != 9) && (modeBits & 0x400);
509 bool H = (layout != 9) && (modeBits & 0x200);
510
511 if (H) {
512 const u32 maxWeights[6] = {9, 11, 15, 19, 23, 31};
513 params.m_MaxWeight = maxWeights[R - 2];
514 } else {
515 const u32 maxWeights[6] = {1, 2, 3, 4, 5, 7};
516 params.m_MaxWeight = maxWeights[R - 2];
517 }
518
519 params.m_bDualPlane = D;
520
521 return params;
522}
523
524static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 blockWidth,
525 u32 blockHeight) {
526 // Don't actually care about the void extent, just read the bits...
527 for (s32 i = 0; i < 4; ++i) {
528 strm.ReadBits<13>();
529 }
530
531 // Decode the RGBA components and renormalize them to the range [0, 255]
532 u16 r = static_cast<u16>(strm.ReadBits<16>());
533 u16 g = static_cast<u16>(strm.ReadBits<16>());
534 u16 b = static_cast<u16>(strm.ReadBits<16>());
535 u16 a = static_cast<u16>(strm.ReadBits<16>());
536
537 u32 rgba = (r >> 8) | (g & 0xFF00) | (static_cast<u32>(b) & 0xFF00) << 8 |
538 (static_cast<u32>(a) & 0xFF00) << 16;
539
540 for (u32 j = 0; j < blockHeight; j++) {
541 for (u32 i = 0; i < blockWidth; i++) {
542 outBuf[j * blockWidth + i] = rgba;
543 }
544 }
545}
546
547static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) {
548 for (u32 j = 0; j < blockHeight; j++) {
549 for (u32 i = 0; i < blockWidth; i++) {
550 outBuf[j * blockWidth + i] = 0xFFFF00FF;
551 }
552 }
553}
554
555static constexpr auto REPLICATE_BYTE_TO_16_TABLE = MakeReplicateTable<u32, 8, 16>();
556static constexpr u32 ReplicateByteTo16(std::size_t value) {
557 return REPLICATE_BYTE_TO_16_TABLE[value];
558}
559
560static constexpr auto REPLICATE_BIT_TO_7_TABLE = MakeReplicateTable<u32, 1, 7>();
561static constexpr u32 ReplicateBitTo7(std::size_t value) {
562 return REPLICATE_BIT_TO_7_TABLE[value];
563}
564
565static constexpr auto REPLICATE_BIT_TO_9_TABLE = MakeReplicateTable<u32, 1, 9>();
566static constexpr u32 ReplicateBitTo9(std::size_t value) {
567 return REPLICATE_BIT_TO_9_TABLE[value];
568}
569
570static constexpr auto REPLICATE_1_BIT_TO_8_TABLE = MakeReplicateTable<u32, 1, 8>();
571static constexpr auto REPLICATE_2_BIT_TO_8_TABLE = MakeReplicateTable<u32, 2, 8>();
572static constexpr auto REPLICATE_3_BIT_TO_8_TABLE = MakeReplicateTable<u32, 3, 8>();
573static constexpr auto REPLICATE_4_BIT_TO_8_TABLE = MakeReplicateTable<u32, 4, 8>();
574static constexpr auto REPLICATE_5_BIT_TO_8_TABLE = MakeReplicateTable<u32, 5, 8>();
575/// Use a precompiled table with the most common usages, if it's not in the expected range, fallback
576/// to the runtime implementation
577static constexpr u32 FastReplicateTo8(u32 value, u32 num_bits) {
578 switch (num_bits) {
579 case 1:
580 return REPLICATE_1_BIT_TO_8_TABLE[value];
581 case 2:
582 return REPLICATE_2_BIT_TO_8_TABLE[value];
583 case 3:
584 return REPLICATE_3_BIT_TO_8_TABLE[value];
585 case 4:
586 return REPLICATE_4_BIT_TO_8_TABLE[value];
587 case 5:
588 return REPLICATE_5_BIT_TO_8_TABLE[value];
589 case 6:
590 return REPLICATE_6_BIT_TO_8_TABLE[value];
591 case 7:
592 return REPLICATE_7_BIT_TO_8_TABLE[value];
593 case 8:
594 return REPLICATE_8_BIT_TO_8_TABLE[value];
595 default:
596 return Replicate(value, num_bits, 8);
597 }
598}
599
600static constexpr auto REPLICATE_1_BIT_TO_6_TABLE = MakeReplicateTable<u32, 1, 6>();
601static constexpr auto REPLICATE_2_BIT_TO_6_TABLE = MakeReplicateTable<u32, 2, 6>();
602static constexpr auto REPLICATE_3_BIT_TO_6_TABLE = MakeReplicateTable<u32, 3, 6>();
603static constexpr auto REPLICATE_4_BIT_TO_6_TABLE = MakeReplicateTable<u32, 4, 6>();
604static constexpr auto REPLICATE_5_BIT_TO_6_TABLE = MakeReplicateTable<u32, 5, 6>();
605static constexpr u32 FastReplicateTo6(u32 value, u32 num_bits) {
606 switch (num_bits) {
607 case 1:
608 return REPLICATE_1_BIT_TO_6_TABLE[value];
609 case 2:
610 return REPLICATE_2_BIT_TO_6_TABLE[value];
611 case 3:
612 return REPLICATE_3_BIT_TO_6_TABLE[value];
613 case 4:
614 return REPLICATE_4_BIT_TO_6_TABLE[value];
615 case 5:
616 return REPLICATE_5_BIT_TO_6_TABLE[value];
617 default:
618 return Replicate(value, num_bits, 6);
619 }
620}
621
622class Pixel {
623protected:
624 using ChannelType = s16;
625 u8 m_BitDepth[4] = {8, 8, 8, 8};
626 s16 color[4] = {};
627
628public:
629 Pixel() = default;
630 Pixel(u32 a, u32 r, u32 g, u32 b, u32 bitDepth = 8)
631 : m_BitDepth{u8(bitDepth), u8(bitDepth), u8(bitDepth), u8(bitDepth)},
632 color{static_cast<ChannelType>(a), static_cast<ChannelType>(r),
633 static_cast<ChannelType>(g), static_cast<ChannelType>(b)} {}
634
635 // Changes the depth of each pixel. This scales the values to
636 // the appropriate bit depth by either truncating the least
637 // significant bits when going from larger to smaller bit depth
638 // or by repeating the most significant bits when going from
639 // smaller to larger bit depths.
640 void ChangeBitDepth() {
641 for (u32 i = 0; i < 4; i++) {
642 Component(i) = ChangeBitDepth(Component(i), m_BitDepth[i]);
643 m_BitDepth[i] = 8;
644 }
645 }
646
647 template <typename IntType>
648 static float ConvertChannelToFloat(IntType channel, u8 bitDepth) {
649 float denominator = static_cast<float>((1 << bitDepth) - 1);
650 return static_cast<float>(channel) / denominator;
651 }
652
653 // Changes the bit depth of a single component. See the comment
654 // above for how we do this.
655 static ChannelType ChangeBitDepth(Pixel::ChannelType val, u8 oldDepth) {
656 assert(oldDepth <= 8);
657
658 if (oldDepth == 8) {
659 // Do nothing
660 return val;
661 } else if (oldDepth == 0) {
662 return static_cast<ChannelType>((1 << 8) - 1);
663 } else if (8 > oldDepth) {
664 return static_cast<ChannelType>(FastReplicateTo8(static_cast<u32>(val), oldDepth));
665 } else {
666 // oldDepth > newDepth
667 const u8 bitsWasted = static_cast<u8>(oldDepth - 8);
668 u16 v = static_cast<u16>(val);
669 v = static_cast<u16>((v + (1 << (bitsWasted - 1))) >> bitsWasted);
670 v = ::std::min<u16>(::std::max<u16>(0, v), static_cast<u16>((1 << 8) - 1));
671 return static_cast<u8>(v);
672 }
673
674 assert(false && "We shouldn't get here.");
675 return 0;
676 }
677
678 const ChannelType& A() const {
679 return color[0];
680 }
681 ChannelType& A() {
682 return color[0];
683 }
684 const ChannelType& R() const {
685 return color[1];
686 }
687 ChannelType& R() {
688 return color[1];
689 }
690 const ChannelType& G() const {
691 return color[2];
692 }
693 ChannelType& G() {
694 return color[2];
695 }
696 const ChannelType& B() const {
697 return color[3];
698 }
699 ChannelType& B() {
700 return color[3];
701 }
702 const ChannelType& Component(u32 idx) const {
703 return color[idx];
704 }
705 ChannelType& Component(u32 idx) {
706 return color[idx];
707 }
708
709 void GetBitDepth(u8 (&outDepth)[4]) const {
710 for (s32 i = 0; i < 4; i++) {
711 outDepth[i] = m_BitDepth[i];
712 }
713 }
714
715 // Take all of the components, transform them to their 8-bit variants,
716 // and then pack each channel into an R8G8B8A8 32-bit integer. We assume
717 // that the architecture is little-endian, so the alpha channel will end
718 // up in the most-significant byte.
719 u32 Pack() const {
720 Pixel eightBit(*this);
721 eightBit.ChangeBitDepth();
722
723 u32 r = 0;
724 r |= eightBit.A();
725 r <<= 8;
726 r |= eightBit.B();
727 r <<= 8;
728 r |= eightBit.G();
729 r <<= 8;
730 r |= eightBit.R();
731 return r;
732 }
733
734 // Clamps the pixel to the range [0,255]
735 void ClampByte() {
736 for (u32 i = 0; i < 4; i++) {
737 color[i] = (color[i] < 0) ? 0 : ((color[i] > 255) ? 255 : color[i]);
738 }
739 }
740
741 void MakeOpaque() {
742 A() = 255;
743 }
744};
745
746static void DecodeColorValues(u32* out, std::span<u8> data, const u32* modes, const u32 nPartitions,
747 const u32 nBitsForColorData) {
748 // First figure out how many color values we have
749 u32 nValues = 0;
750 for (u32 i = 0; i < nPartitions; i++) {
751 nValues += ((modes[i] >> 2) + 1) << 1;
752 }
753
754 // Then based on the number of values and the remaining number of bits,
755 // figure out the max value for each of them...
756 u32 range = 256;
757 while (--range > 0) {
758 IntegerEncodedValue val = ASTC_ENCODINGS_VALUES[range];
759 u32 bitLength = val.GetBitLength(nValues);
760 if (bitLength <= nBitsForColorData) {
761 // Find the smallest possible range that matches the given encoding
762 while (--range > 0) {
763 IntegerEncodedValue newval = ASTC_ENCODINGS_VALUES[range];
764 if (!newval.MatchesEncoding(val)) {
765 break;
766 }
767 }
768
769 // Return to last matching range.
770 range++;
771 break;
772 }
773 }
774
775 // We now have enough to decode our integer sequence.
776 IntegerEncodedVector decodedColorValues;
777
778 InputBitStream colorStream(data, 0);
779 DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues);
780
781 // Once we have the decoded values, we need to dequantize them to the 0-255 range
782 // This procedure is outlined in ASTC spec C.2.13
783 u32 outIdx = 0;
784 for (auto itr = decodedColorValues.begin(); itr != decodedColorValues.end(); ++itr) {
785 // Have we already decoded all that we need?
786 if (outIdx >= nValues) {
787 break;
788 }
789
790 const IntegerEncodedValue& val = *itr;
791 u32 bitlen = val.num_bits;
792 u32 bitval = val.bit_value;
793
794 assert(bitlen >= 1);
795
796 u32 A = 0, B = 0, C = 0, D = 0;
797 // A is just the lsb replicated 9 times.
798 A = ReplicateBitTo9(bitval & 1);
799
800 switch (val.encoding) {
801 // Replicate bits
802 case IntegerEncoding::JustBits:
803 out[outIdx++] = FastReplicateTo8(bitval, bitlen);
804 break;
805
806 // Use algorithm in C.2.13
807 case IntegerEncoding::Trit: {
808
809 D = val.trit_value;
810
811 switch (bitlen) {
812 case 1: {
813 C = 204;
814 } break;
815
816 case 2: {
817 C = 93;
818 // B = b000b0bb0
819 u32 b = (bitval >> 1) & 1;
820 B = (b << 8) | (b << 4) | (b << 2) | (b << 1);
821 } break;
822
823 case 3: {
824 C = 44;
825 // B = cb000cbcb
826 u32 cb = (bitval >> 1) & 3;
827 B = (cb << 7) | (cb << 2) | cb;
828 } break;
829
830 case 4: {
831 C = 22;
832 // B = dcb000dcb
833 u32 dcb = (bitval >> 1) & 7;
834 B = (dcb << 6) | dcb;
835 } break;
836
837 case 5: {
838 C = 11;
839 // B = edcb000ed
840 u32 edcb = (bitval >> 1) & 0xF;
841 B = (edcb << 5) | (edcb >> 2);
842 } break;
843
844 case 6: {
845 C = 5;
846 // B = fedcb000f
847 u32 fedcb = (bitval >> 1) & 0x1F;
848 B = (fedcb << 4) | (fedcb >> 4);
849 } break;
850
851 default:
852 assert(false && "Unsupported trit encoding for color values!");
853 break;
854 } // switch(bitlen)
855 } // case IntegerEncoding::Trit
856 break;
857
858 case IntegerEncoding::Quint: {
859
860 D = val.quint_value;
861
862 switch (bitlen) {
863 case 1: {
864 C = 113;
865 } break;
866
867 case 2: {
868 C = 54;
869 // B = b0000bb00
870 u32 b = (bitval >> 1) & 1;
871 B = (b << 8) | (b << 3) | (b << 2);
872 } break;
873
874 case 3: {
875 C = 26;
876 // B = cb0000cbc
877 u32 cb = (bitval >> 1) & 3;
878 B = (cb << 7) | (cb << 1) | (cb >> 1);
879 } break;
880
881 case 4: {
882 C = 13;
883 // B = dcb0000dc
884 u32 dcb = (bitval >> 1) & 7;
885 B = (dcb << 6) | (dcb >> 1);
886 } break;
887
888 case 5: {
889 C = 6;
890 // B = edcb0000e
891 u32 edcb = (bitval >> 1) & 0xF;
892 B = (edcb << 5) | (edcb >> 3);
893 } break;
894
895 default:
896 assert(false && "Unsupported quint encoding for color values!");
897 break;
898 } // switch(bitlen)
899 } // case IntegerEncoding::Quint
900 break;
901 } // switch(val.encoding)
902
903 if (val.encoding != IntegerEncoding::JustBits) {
904 u32 T = D * C + B;
905 T ^= A;
906 T = (A & 0x80) | (T >> 2);
907 out[outIdx++] = T;
908 }
909 }
910
911 // Make sure that each of our values is in the proper range...
912 for (u32 i = 0; i < nValues; i++) {
913 assert(out[i] <= 255);
914 }
915}
916
917static u32 UnquantizeTexelWeight(const IntegerEncodedValue& val) {
918 u32 bitval = val.bit_value;
919 u32 bitlen = val.num_bits;
920
921 u32 A = ReplicateBitTo7(bitval & 1);
922 u32 B = 0, C = 0, D = 0;
923
924 u32 result = 0;
925 switch (val.encoding) {
926 case IntegerEncoding::JustBits:
927 result = FastReplicateTo6(bitval, bitlen);
928 break;
929
930 case IntegerEncoding::Trit: {
931 D = val.trit_value;
932 assert(D < 3);
933
934 switch (bitlen) {
935 case 0: {
936 u32 results[3] = {0, 32, 63};
937 result = results[D];
938 } break;
939
940 case 1: {
941 C = 50;
942 } break;
943
944 case 2: {
945 C = 23;
946 u32 b = (bitval >> 1) & 1;
947 B = (b << 6) | (b << 2) | b;
948 } break;
949
950 case 3: {
951 C = 11;
952 u32 cb = (bitval >> 1) & 3;
953 B = (cb << 5) | cb;
954 } break;
955
956 default:
957 assert(false && "Invalid trit encoding for texel weight");
958 break;
959 }
960 } break;
961
962 case IntegerEncoding::Quint: {
963 D = val.quint_value;
964 assert(D < 5);
965
966 switch (bitlen) {
967 case 0: {
968 u32 results[5] = {0, 16, 32, 47, 63};
969 result = results[D];
970 } break;
971
972 case 1: {
973 C = 28;
974 } break;
975
976 case 2: {
977 C = 13;
978 u32 b = (bitval >> 1) & 1;
979 B = (b << 6) | (b << 1);
980 } break;
981
982 default:
983 assert(false && "Invalid quint encoding for texel weight");
984 break;
985 }
986 } break;
987 }
988
989 if (val.encoding != IntegerEncoding::JustBits && bitlen > 0) {
990 // Decode the value...
991 result = D * C + B;
992 result ^= A;
993 result = (A & 0x20) | (result >> 2);
994 }
995
996 assert(result < 64);
997
998 // Change from [0,63] to [0,64]
999 if (result > 32) {
1000 result += 1;
1001 }
1002
1003 return result;
1004}
1005
1006static void UnquantizeTexelWeights(u32 out[2][144], const IntegerEncodedVector& weights,
1007 const TexelWeightParams& params, const u32 blockWidth,
1008 const u32 blockHeight) {
1009 u32 weightIdx = 0;
1010 u32 unquantized[2][144];
1011
1012 for (auto itr = weights.begin(); itr != weights.end(); ++itr) {
1013 unquantized[0][weightIdx] = UnquantizeTexelWeight(*itr);
1014
1015 if (params.m_bDualPlane) {
1016 ++itr;
1017 unquantized[1][weightIdx] = UnquantizeTexelWeight(*itr);
1018 if (itr == weights.end()) {
1019 break;
1020 }
1021 }
1022
1023 if (++weightIdx >= (params.m_Width * params.m_Height))
1024 break;
1025 }
1026
1027 // Do infill if necessary (Section C.2.18) ...
1028 u32 Ds = (1024 + (blockWidth / 2)) / (blockWidth - 1);
1029 u32 Dt = (1024 + (blockHeight / 2)) / (blockHeight - 1);
1030
1031 const u32 kPlaneScale = params.m_bDualPlane ? 2U : 1U;
1032 for (u32 plane = 0; plane < kPlaneScale; plane++)
1033 for (u32 t = 0; t < blockHeight; t++)
1034 for (u32 s = 0; s < blockWidth; s++) {
1035 u32 cs = Ds * s;
1036 u32 ct = Dt * t;
1037
1038 u32 gs = (cs * (params.m_Width - 1) + 32) >> 6;
1039 u32 gt = (ct * (params.m_Height - 1) + 32) >> 6;
1040
1041 u32 js = gs >> 4;
1042 u32 fs = gs & 0xF;
1043
1044 u32 jt = gt >> 4;
1045 u32 ft = gt & 0x0F;
1046
1047 u32 w11 = (fs * ft + 8) >> 4;
1048 u32 w10 = ft - w11;
1049 u32 w01 = fs - w11;
1050 u32 w00 = 16 - fs - ft + w11;
1051
1052 u32 v0 = js + jt * params.m_Width;
1053
1054#define FIND_TEXEL(tidx, bidx) \
1055 u32 p##bidx = 0; \
1056 do { \
1057 if ((tidx) < (params.m_Width * params.m_Height)) { \
1058 p##bidx = unquantized[plane][(tidx)]; \
1059 } \
1060 } while (0)
1061
1062 FIND_TEXEL(v0, 00);
1063 FIND_TEXEL(v0 + 1, 01);
1064 FIND_TEXEL(v0 + params.m_Width, 10);
1065 FIND_TEXEL(v0 + params.m_Width + 1, 11);
1066
1067#undef FIND_TEXEL
1068
1069 out[plane][t * blockWidth + s] =
1070 (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4;
1071 }
1072}
1073
1074// Transfers a bit as described in C.2.14
1075static inline void BitTransferSigned(int& a, int& b) {
1076 b >>= 1;
1077 b |= a & 0x80;
1078 a >>= 1;
1079 a &= 0x3F;
1080 if (a & 0x20)
1081 a -= 0x40;
1082}
1083
1084// Adds more precision to the blue channel as described
1085// in C.2.14
1086static inline Pixel BlueContract(s32 a, s32 r, s32 g, s32 b) {
1087 return Pixel(static_cast<s16>(a), static_cast<s16>((r + b) >> 1),
1088 static_cast<s16>((g + b) >> 1), static_cast<s16>(b));
1089}
1090
1091// Partition selection functions as specified in
1092// C.2.21
1093static inline u32 hash52(u32 p) {
1094 p ^= p >> 15;
1095 p -= p << 17;
1096 p += p << 7;
1097 p += p << 4;
1098 p ^= p >> 5;
1099 p += p << 16;
1100 p ^= p >> 7;
1101 p ^= p >> 3;
1102 p ^= p << 6;
1103 p ^= p >> 17;
1104 return p;
1105}
1106
1107static u32 SelectPartition(s32 seed, s32 x, s32 y, s32 z, s32 partitionCount, s32 smallBlock) {
1108 if (1 == partitionCount)
1109 return 0;
1110
1111 if (smallBlock) {
1112 x <<= 1;
1113 y <<= 1;
1114 z <<= 1;
1115 }
1116
1117 seed += (partitionCount - 1) * 1024;
1118
1119 u32 rnum = hash52(static_cast<u32>(seed));
1120 u8 seed1 = static_cast<u8>(rnum & 0xF);
1121 u8 seed2 = static_cast<u8>((rnum >> 4) & 0xF);
1122 u8 seed3 = static_cast<u8>((rnum >> 8) & 0xF);
1123 u8 seed4 = static_cast<u8>((rnum >> 12) & 0xF);
1124 u8 seed5 = static_cast<u8>((rnum >> 16) & 0xF);
1125 u8 seed6 = static_cast<u8>((rnum >> 20) & 0xF);
1126 u8 seed7 = static_cast<u8>((rnum >> 24) & 0xF);
1127 u8 seed8 = static_cast<u8>((rnum >> 28) & 0xF);
1128 u8 seed9 = static_cast<u8>((rnum >> 18) & 0xF);
1129 u8 seed10 = static_cast<u8>((rnum >> 22) & 0xF);
1130 u8 seed11 = static_cast<u8>((rnum >> 26) & 0xF);
1131 u8 seed12 = static_cast<u8>(((rnum >> 30) | (rnum << 2)) & 0xF);
1132
1133 seed1 = static_cast<u8>(seed1 * seed1);
1134 seed2 = static_cast<u8>(seed2 * seed2);
1135 seed3 = static_cast<u8>(seed3 * seed3);
1136 seed4 = static_cast<u8>(seed4 * seed4);
1137 seed5 = static_cast<u8>(seed5 * seed5);
1138 seed6 = static_cast<u8>(seed6 * seed6);
1139 seed7 = static_cast<u8>(seed7 * seed7);
1140 seed8 = static_cast<u8>(seed8 * seed8);
1141 seed9 = static_cast<u8>(seed9 * seed9);
1142 seed10 = static_cast<u8>(seed10 * seed10);
1143 seed11 = static_cast<u8>(seed11 * seed11);
1144 seed12 = static_cast<u8>(seed12 * seed12);
1145
1146 s32 sh1, sh2, sh3;
1147 if (seed & 1) {
1148 sh1 = (seed & 2) ? 4 : 5;
1149 sh2 = (partitionCount == 3) ? 6 : 5;
1150 } else {
1151 sh1 = (partitionCount == 3) ? 6 : 5;
1152 sh2 = (seed & 2) ? 4 : 5;
1153 }
1154 sh3 = (seed & 0x10) ? sh1 : sh2;
1155
1156 seed1 = static_cast<u8>(seed1 >> sh1);
1157 seed2 = static_cast<u8>(seed2 >> sh2);
1158 seed3 = static_cast<u8>(seed3 >> sh1);
1159 seed4 = static_cast<u8>(seed4 >> sh2);
1160 seed5 = static_cast<u8>(seed5 >> sh1);
1161 seed6 = static_cast<u8>(seed6 >> sh2);
1162 seed7 = static_cast<u8>(seed7 >> sh1);
1163 seed8 = static_cast<u8>(seed8 >> sh2);
1164 seed9 = static_cast<u8>(seed9 >> sh3);
1165 seed10 = static_cast<u8>(seed10 >> sh3);
1166 seed11 = static_cast<u8>(seed11 >> sh3);
1167 seed12 = static_cast<u8>(seed12 >> sh3);
1168
1169 s32 a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14);
1170 s32 b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10);
1171 s32 c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6);
1172 s32 d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2);
1173
1174 a &= 0x3F;
1175 b &= 0x3F;
1176 c &= 0x3F;
1177 d &= 0x3F;
1178
1179 if (partitionCount < 4)
1180 d = 0;
1181 if (partitionCount < 3)
1182 c = 0;
1183
1184 if (a >= b && a >= c && a >= d)
1185 return 0;
1186 else if (b >= c && b >= d)
1187 return 1;
1188 else if (c >= d)
1189 return 2;
1190 return 3;
1191}
1192
1193static inline u32 Select2DPartition(s32 seed, s32 x, s32 y, s32 partitionCount, s32 smallBlock) {
1194 return SelectPartition(seed, x, y, 0, partitionCount, smallBlock);
1195}
1196
1197// Section C.2.14
1198static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const u32*& colorValues,
1199 u32 colorEndpointMode) {
1200#define READ_UINT_VALUES(N) \
1201 u32 v[N]; \
1202 for (u32 i = 0; i < N; i++) { \
1203 v[i] = *(colorValues++); \
1204 }
1205
1206#define READ_INT_VALUES(N) \
1207 s32 v[N]; \
1208 for (u32 i = 0; i < N; i++) { \
1209 v[i] = static_cast<int>(*(colorValues++)); \
1210 }
1211
1212 switch (colorEndpointMode) {
1213 case 0: {
1214 READ_UINT_VALUES(2)
1215 ep1 = Pixel(0xFF, v[0], v[0], v[0]);
1216 ep2 = Pixel(0xFF, v[1], v[1], v[1]);
1217 } break;
1218
1219 case 1: {
1220 READ_UINT_VALUES(2)
1221 u32 L0 = (v[0] >> 2) | (v[1] & 0xC0);
1222 u32 L1 = std::min(L0 + (v[1] & 0x3F), 0xFFU);
1223 ep1 = Pixel(0xFF, L0, L0, L0);
1224 ep2 = Pixel(0xFF, L1, L1, L1);
1225 } break;
1226
1227 case 4: {
1228 READ_UINT_VALUES(4)
1229 ep1 = Pixel(v[2], v[0], v[0], v[0]);
1230 ep2 = Pixel(v[3], v[1], v[1], v[1]);
1231 } break;
1232
1233 case 5: {
1234 READ_INT_VALUES(4)
1235 BitTransferSigned(v[1], v[0]);
1236 BitTransferSigned(v[3], v[2]);
1237 ep1 = Pixel(v[2], v[0], v[0], v[0]);
1238 ep2 = Pixel(v[2] + v[3], v[0] + v[1], v[0] + v[1], v[0] + v[1]);
1239 ep1.ClampByte();
1240 ep2.ClampByte();
1241 } break;
1242
1243 case 6: {
1244 READ_UINT_VALUES(4)
1245 ep1 = Pixel(0xFF, v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8);
1246 ep2 = Pixel(0xFF, v[0], v[1], v[2]);
1247 } break;
1248
1249 case 8: {
1250 READ_UINT_VALUES(6)
1251 if (v[1] + v[3] + v[5] >= v[0] + v[2] + v[4]) {
1252 ep1 = Pixel(0xFF, v[0], v[2], v[4]);
1253 ep2 = Pixel(0xFF, v[1], v[3], v[5]);
1254 } else {
1255 ep1 = BlueContract(0xFF, v[1], v[3], v[5]);
1256 ep2 = BlueContract(0xFF, v[0], v[2], v[4]);
1257 }
1258 } break;
1259
1260 case 9: {
1261 READ_INT_VALUES(6)
1262 BitTransferSigned(v[1], v[0]);
1263 BitTransferSigned(v[3], v[2]);
1264 BitTransferSigned(v[5], v[4]);
1265 if (v[1] + v[3] + v[5] >= 0) {
1266 ep1 = Pixel(0xFF, v[0], v[2], v[4]);
1267 ep2 = Pixel(0xFF, v[0] + v[1], v[2] + v[3], v[4] + v[5]);
1268 } else {
1269 ep1 = BlueContract(0xFF, v[0] + v[1], v[2] + v[3], v[4] + v[5]);
1270 ep2 = BlueContract(0xFF, v[0], v[2], v[4]);
1271 }
1272 ep1.ClampByte();
1273 ep2.ClampByte();
1274 } break;
1275
1276 case 10: {
1277 READ_UINT_VALUES(6)
1278 ep1 = Pixel(v[4], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8);
1279 ep2 = Pixel(v[5], v[0], v[1], v[2]);
1280 } break;
1281
1282 case 12: {
1283 READ_UINT_VALUES(8)
1284 if (v[1] + v[3] + v[5] >= v[0] + v[2] + v[4]) {
1285 ep1 = Pixel(v[6], v[0], v[2], v[4]);
1286 ep2 = Pixel(v[7], v[1], v[3], v[5]);
1287 } else {
1288 ep1 = BlueContract(v[7], v[1], v[3], v[5]);
1289 ep2 = BlueContract(v[6], v[0], v[2], v[4]);
1290 }
1291 } break;
1292
1293 case 13: {
1294 READ_INT_VALUES(8)
1295 BitTransferSigned(v[1], v[0]);
1296 BitTransferSigned(v[3], v[2]);
1297 BitTransferSigned(v[5], v[4]);
1298 BitTransferSigned(v[7], v[6]);
1299 if (v[1] + v[3] + v[5] >= 0) {
1300 ep1 = Pixel(v[6], v[0], v[2], v[4]);
1301 ep2 = Pixel(v[7] + v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5]);
1302 } else {
1303 ep1 = BlueContract(v[6] + v[7], v[0] + v[1], v[2] + v[3], v[4] + v[5]);
1304 ep2 = BlueContract(v[6], v[0], v[2], v[4]);
1305 }
1306 ep1.ClampByte();
1307 ep2.ClampByte();
1308 } break;
1309
1310 default:
1311 assert(false && "Unsupported color endpoint mode (is it HDR?)");
1312 break;
1313 }
1314
1315#undef READ_UINT_VALUES
1316#undef READ_INT_VALUES
1317}
1318
1319static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
1320 const u32 blockHeight, std::span<u32, 12 * 12> outBuf) {
1321 InputBitStream strm(inBuf);
1322 TexelWeightParams weightParams = DecodeBlockInfo(strm);
1323
1324 // Was there an error?
1325 if (weightParams.m_bError) {
1326 assert(false && "Invalid block mode");
1327 FillError(outBuf, blockWidth, blockHeight);
1328 return;
1329 }
1330
1331 if (weightParams.m_bVoidExtentLDR) {
1332 FillVoidExtentLDR(strm, outBuf, blockWidth, blockHeight);
1333 return;
1334 }
1335
1336 if (weightParams.m_bVoidExtentHDR) {
1337 assert(false && "HDR void extent blocks are unsupported!");
1338 FillError(outBuf, blockWidth, blockHeight);
1339 return;
1340 }
1341
1342 if (weightParams.m_Width > blockWidth) {
1343 assert(false && "Texel weight grid width should be smaller than block width");
1344 FillError(outBuf, blockWidth, blockHeight);
1345 return;
1346 }
1347
1348 if (weightParams.m_Height > blockHeight) {
1349 assert(false && "Texel weight grid height should be smaller than block height");
1350 FillError(outBuf, blockWidth, blockHeight);
1351 return;
1352 }
1353
1354 // Read num partitions
1355 u32 nPartitions = strm.ReadBits<2>() + 1;
1356 assert(nPartitions <= 4);
1357
1358 if (nPartitions == 4 && weightParams.m_bDualPlane) {
1359 assert(false && "Dual plane mode is incompatible with four partition blocks");
1360 FillError(outBuf, blockWidth, blockHeight);
1361 return;
1362 }
1363
1364 // Based on the number of partitions, read the color endpoint mode for
1365 // each partition.
1366
1367 // Determine partitions, partition index, and color endpoint modes
1368 s32 planeIdx = -1;
1369 u32 partitionIndex;
1370 u32 colorEndpointMode[4] = {0, 0, 0, 0};
1371
1372 // Define color data.
1373 u8 colorEndpointData[16];
1374 memset(colorEndpointData, 0, sizeof(colorEndpointData));
1375 OutputBitStream colorEndpointStream(colorEndpointData, 16 * 8, 0);
1376
1377 // Read extra config data...
1378 u32 baseCEM = 0;
1379 if (nPartitions == 1) {
1380 colorEndpointMode[0] = strm.ReadBits<4>();
1381 partitionIndex = 0;
1382 } else {
1383 partitionIndex = strm.ReadBits<10>();
1384 baseCEM = strm.ReadBits<6>();
1385 }
1386 u32 baseMode = (baseCEM & 3);
1387
1388 // Remaining bits are color endpoint data...
1389 u32 nWeightBits = weightParams.GetPackedBitSize();
1390 s32 remainingBits = 128 - nWeightBits - static_cast<int>(strm.GetBitsRead());
1391
1392 // Consider extra bits prior to texel data...
1393 u32 extraCEMbits = 0;
1394 if (baseMode) {
1395 switch (nPartitions) {
1396 case 2:
1397 extraCEMbits += 2;
1398 break;
1399 case 3:
1400 extraCEMbits += 5;
1401 break;
1402 case 4:
1403 extraCEMbits += 8;
1404 break;
1405 default:
1406 assert(false);
1407 break;
1408 }
1409 }
1410 remainingBits -= extraCEMbits;
1411
1412 // Do we have a dual plane situation?
1413 u32 planeSelectorBits = 0;
1414 if (weightParams.m_bDualPlane) {
1415 planeSelectorBits = 2;
1416 }
1417 remainingBits -= planeSelectorBits;
1418
1419 // Read color data...
1420 u32 colorDataBits = remainingBits;
1421 while (remainingBits > 0) {
1422 u32 nb = std::min(remainingBits, 8);
1423 u32 b = strm.ReadBits(nb);
1424 colorEndpointStream.WriteBits(b, nb);
1425 remainingBits -= 8;
1426 }
1427
1428 // Read the plane selection bits
1429 planeIdx = strm.ReadBits(planeSelectorBits);
1430
1431 // Read the rest of the CEM
1432 if (baseMode) {
1433 u32 extraCEM = strm.ReadBits(extraCEMbits);
1434 u32 CEM = (extraCEM << 6) | baseCEM;
1435 CEM >>= 2;
1436
1437 bool C[4] = {0};
1438 for (u32 i = 0; i < nPartitions; i++) {
1439 C[i] = CEM & 1;
1440 CEM >>= 1;
1441 }
1442
1443 u8 M[4] = {0};
1444 for (u32 i = 0; i < nPartitions; i++) {
1445 M[i] = CEM & 3;
1446 CEM >>= 2;
1447 assert(M[i] <= 3);
1448 }
1449
1450 for (u32 i = 0; i < nPartitions; i++) {
1451 colorEndpointMode[i] = baseMode;
1452 if (!(C[i]))
1453 colorEndpointMode[i] -= 1;
1454 colorEndpointMode[i] <<= 2;
1455 colorEndpointMode[i] |= M[i];
1456 }
1457 } else if (nPartitions > 1) {
1458 u32 CEM = baseCEM >> 2;
1459 for (u32 i = 0; i < nPartitions; i++) {
1460 colorEndpointMode[i] = CEM;
1461 }
1462 }
1463
1464 // Make sure everything up till here is sane.
1465 for (u32 i = 0; i < nPartitions; i++) {
1466 assert(colorEndpointMode[i] < 16);
1467 }
1468 assert(strm.GetBitsRead() + weightParams.GetPackedBitSize() == 128);
1469
1470 // Decode both color data and texel weight data
1471 u32 colorValues[32]; // Four values, two endpoints, four maximum paritions
1472 DecodeColorValues(colorValues, colorEndpointData, colorEndpointMode, nPartitions,
1473 colorDataBits);
1474
1475 Pixel endpoints[4][2];
1476 const u32* colorValuesPtr = colorValues;
1477 for (u32 i = 0; i < nPartitions; i++) {
1478 ComputeEndpoints(endpoints[i][0], endpoints[i][1], colorValuesPtr, colorEndpointMode[i]);
1479 }
1480
1481 // Read the texel weight data..
1482 std::array<u8, 16> texelWeightData;
1483 std::ranges::copy(inBuf, texelWeightData.begin());
1484
1485 // Reverse everything
1486 for (u32 i = 0; i < 8; i++) {
1487// Taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits
1488#define REVERSE_BYTE(b) (((b)*0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32
1489 u8 a = static_cast<u8>(REVERSE_BYTE(texelWeightData[i]));
1490 u8 b = static_cast<u8>(REVERSE_BYTE(texelWeightData[15 - i]));
1491#undef REVERSE_BYTE
1492
1493 texelWeightData[i] = b;
1494 texelWeightData[15 - i] = a;
1495 }
1496
1497 // Make sure that higher non-texel bits are set to zero
1498 const u32 clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1;
1499 if (clearByteStart > 0 && clearByteStart <= texelWeightData.size()) {
1500 texelWeightData[clearByteStart - 1] &=
1501 static_cast<u8>((1 << (weightParams.GetPackedBitSize() % 8)) - 1);
1502 std::memset(texelWeightData.data() + clearByteStart, 0,
1503 std::min(16U - clearByteStart, 16U));
1504 }
1505
1506 IntegerEncodedVector texelWeightValues;
1507
1508 InputBitStream weightStream(texelWeightData);
1509
1510 DecodeIntegerSequence(texelWeightValues, weightStream, weightParams.m_MaxWeight,
1511 weightParams.GetNumWeightValues());
1512
1513 // Blocks can be at most 12x12, so we can have as many as 144 weights
1514 u32 weights[2][144];
1515 UnquantizeTexelWeights(weights, texelWeightValues, weightParams, blockWidth, blockHeight);
1516
1517 // Now that we have endpoints and weights, we can interpolate and generate
1518 // the proper decoding...
1519 for (u32 j = 0; j < blockHeight; j++)
1520 for (u32 i = 0; i < blockWidth; i++) {
1521 u32 partition = Select2DPartition(partitionIndex, i, j, nPartitions,
1522 (blockHeight * blockWidth) < 32);
1523 assert(partition < nPartitions);
1524
1525 Pixel p;
1526 for (u32 c = 0; c < 4; c++) {
1527 u32 C0 = endpoints[partition][0].Component(c);
1528 C0 = ReplicateByteTo16(C0);
1529 u32 C1 = endpoints[partition][1].Component(c);
1530 C1 = ReplicateByteTo16(C1);
1531
1532 u32 plane = 0;
1533 if (weightParams.m_bDualPlane && (((planeIdx + 1) & 3) == c)) {
1534 plane = 1;
1535 }
1536
1537 u32 weight = weights[plane][j * blockWidth + i];
1538 u32 C = (C0 * (64 - weight) + C1 * weight + 32) / 64;
1539 if (C == 65535) {
1540 p.Component(c) = 255;
1541 } else {
1542 double Cf = static_cast<double>(C);
1543 p.Component(c) = static_cast<u16>(255.0 * (Cf / 65536.0) + 0.5);
1544 }
1545 }
1546
1547 outBuf[j * blockWidth + i] = p.Pack();
1548 }
1549}
1550
1551void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth,
1552 uint32_t block_width, uint32_t block_height, std::span<uint8_t> output) {
1553 u32 block_index = 0;
1554 std::size_t depth_offset = 0;
1555 for (u32 z = 0; z < depth; z++) {
1556 for (u32 y = 0; y < height; y += block_height) {
1557 for (u32 x = 0; x < width; x += block_width) {
1558 const std::span<const u8, 16> blockPtr{data.subspan(block_index * 16, 16)};
1559
1560 // Blocks can be at most 12x12
1561 std::array<u32, 12 * 12> uncompData;
1562 DecompressBlock(blockPtr, block_width, block_height, uncompData);
1563
1564 u32 decompWidth = std::min(block_width, width - x);
1565 u32 decompHeight = std::min(block_height, height - y);
1566
1567 const std::span<u8> outRow = output.subspan(depth_offset + (y * width + x) * 4);
1568 for (u32 jj = 0; jj < decompHeight; jj++) {
1569 std::memcpy(outRow.data() + jj * width * 4,
1570 uncompData.data() + jj * block_width, decompWidth * 4);
1571 }
1572 ++block_index;
1573 }
1574 }
1575 depth_offset += height * width * 4;
1576 }
1577}
1578
1579} // namespace Tegra::Texture::ASTC
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
index c1c73fda5..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,17 +116,11 @@ 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 { 123void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth,
125 decltype(EncodingsValues) encoding_values = EncodingsValues; 124 uint32_t block_width, uint32_t block_height, std::span<uint8_t> output);
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 125
132} // namespace Tegra::Texture::ASTC 126} // namespace Tegra::Texture::ASTC
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_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
index 5c64c9bf7..0f60765bb 100644
--- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
@@ -12,6 +12,14 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
12 VkDebugUtilsMessageTypeFlagsEXT type, 12 VkDebugUtilsMessageTypeFlagsEXT type,
13 const VkDebugUtilsMessengerCallbackDataEXT* data, 13 const VkDebugUtilsMessengerCallbackDataEXT* data,
14 [[maybe_unused]] void* user_data) { 14 [[maybe_unused]] void* user_data) {
15 // Skip logging known false-positive validation errors
16 switch (static_cast<u32>(data->messageIdNumber)) {
17 case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter
18 case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0])
19 return VK_FALSE;
20 default:
21 break;
22 }
15 const std::string_view message{data->pMessage}; 23 const std::string_view message{data->pMessage};
16 if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { 24 if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
17 LOG_CRITICAL(Render_Vulkan, "{}", message); 25 LOG_CRITICAL(Render_Vulkan, "{}", message);
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 64206b3d2..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
@@ -531,6 +532,27 @@ bool Device::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags want
531 return (supported_usage & wanted_usage) == wanted_usage; 532 return (supported_usage & wanted_usage) == wanted_usage;
532} 533}
533 534
535std::string Device::GetDriverName() const {
536 switch (driver_id) {
537 case VK_DRIVER_ID_AMD_PROPRIETARY:
538 return "AMD";
539 case VK_DRIVER_ID_AMD_OPEN_SOURCE:
540 return "AMDVLK";
541 case VK_DRIVER_ID_MESA_RADV:
542 return "RADV";
543 case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
544 return "NVIDIA";
545 case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
546 return "INTEL";
547 case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
548 return "ANV";
549 case VK_DRIVER_ID_MESA_LLVMPIPE:
550 return "LAVAPIPE";
551 default:
552 return vendor_name;
553 }
554}
555
534void Device::CheckSuitability(bool requires_swapchain) const { 556void Device::CheckSuitability(bool requires_swapchain) const {
535 std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; 557 std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions;
536 bool has_swapchain = false; 558 bool has_swapchain = false;
@@ -818,6 +840,17 @@ void Device::CollectTelemetryParameters() {
818 } 840 }
819} 841}
820 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
821void Device::CollectToolingInfo() { 854void Device::CollectToolingInfo() {
822 if (!ext_tooling_info) { 855 if (!ext_tooling_info) {
823 return; 856 return;
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 67d70cd22..96c0f8c60 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -45,6 +45,9 @@ public:
45 /// Reports a shader to Nsight Aftermath. 45 /// Reports a shader to Nsight Aftermath.
46 void SaveShader(const std::vector<u32>& spirv) const; 46 void SaveShader(const std::vector<u32>& spirv) const;
47 47
48 /// Returns the name of the VkDriverId reported from Vulkan.
49 std::string GetDriverName() const;
50
48 /// Returns the dispatch loader with direct function pointers of the device. 51 /// Returns the dispatch loader with direct function pointers of the device.
49 const vk::DeviceDispatch& GetDispatchLoader() const { 52 const vk::DeviceDispatch& GetDispatchLoader() const {
50 return dld; 53 return dld;
@@ -225,6 +228,10 @@ public:
225 return use_asynchronous_shaders; 228 return use_asynchronous_shaders;
226 } 229 }
227 230
231 u64 GetDeviceLocalMemory() const {
232 return device_access_memory;
233 }
234
228private: 235private:
229 /// Checks if the physical device is suitable. 236 /// Checks if the physical device is suitable.
230 void CheckSuitability(bool requires_swapchain) const; 237 void CheckSuitability(bool requires_swapchain) const;
@@ -244,6 +251,9 @@ private:
244 /// Collects information about attached tools. 251 /// Collects information about attached tools.
245 void CollectToolingInfo(); 252 void CollectToolingInfo();
246 253
254 /// Collects information about the device's local memory.
255 void CollectPhysicalMemoryInfo();
256
247 /// Returns a list of queue initialization descriptors. 257 /// Returns a list of queue initialization descriptors.
248 std::vector<VkDeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const; 258 std::vector<VkDeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
249 259
@@ -257,21 +267,22 @@ private:
257 bool IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, 267 bool IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
258 FormatType format_type) const; 268 FormatType format_type) const;
259 269
260 VkInstance instance; ///< Vulkan instance. 270 VkInstance instance; ///< Vulkan instance.
261 vk::DeviceDispatch dld; ///< Device function pointers. 271 vk::DeviceDispatch dld; ///< Device function pointers.
262 vk::PhysicalDevice physical; ///< Physical device. 272 vk::PhysicalDevice physical; ///< Physical device.
263 VkPhysicalDeviceProperties properties; ///< Device properties. 273 VkPhysicalDeviceProperties properties; ///< Device properties.
264 vk::Device logical; ///< Logical device. 274 vk::Device logical; ///< Logical device.
265 vk::Queue graphics_queue; ///< Main graphics queue. 275 vk::Queue graphics_queue; ///< Main graphics queue.
266 vk::Queue present_queue; ///< Main present queue. 276 vk::Queue present_queue; ///< Main present queue.
267 u32 instance_version{}; ///< Vulkan onstance version. 277 u32 instance_version{}; ///< Vulkan onstance version.
268 u32 graphics_family{}; ///< Main graphics queue family index. 278 u32 graphics_family{}; ///< Main graphics queue family index.
269 u32 present_family{}; ///< Main present queue family index. 279 u32 present_family{}; ///< Main present queue family index.
270 VkDriverIdKHR driver_id{}; ///< Driver ID. 280 VkDriverIdKHR driver_id{}; ///< Driver ID.
271 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.
272 bool is_optimal_astc_supported{}; ///< Support for native ASTC. 282 u64 device_access_memory{}; ///< Total size of device local memory in bytes.
273 bool is_float16_supported{}; ///< Support for float16 arithmetics. 283 bool is_optimal_astc_supported{}; ///< Support for native ASTC.
274 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.
275 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.
276 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.
277 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 a59b36e13..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}},
@@ -647,6 +648,8 @@ void Config::ReadDebuggingValues() {
647 ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString(); 648 ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString();
648 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); 649 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool();
649 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); 650 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool();
651 Settings::values.enable_fs_access_log =
652 ReadSetting(QStringLiteral("enable_fs_access_log"), false).toBool();
650 Settings::values.reporting_services = 653 Settings::values.reporting_services =
651 ReadSetting(QStringLiteral("reporting_services"), false).toBool(); 654 ReadSetting(QStringLiteral("reporting_services"), false).toBool();
652 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); 655 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
@@ -754,6 +757,8 @@ void Config::ReadCpuValues() {
754 QStringLiteral("cpuopt_unsafe_unfuse_fma"), true); 757 QStringLiteral("cpuopt_unsafe_unfuse_fma"), true);
755 ReadSettingGlobal(Settings::values.cpuopt_unsafe_reduce_fp_error, 758 ReadSettingGlobal(Settings::values.cpuopt_unsafe_reduce_fp_error,
756 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);
757 ReadSettingGlobal(Settings::values.cpuopt_unsafe_inaccurate_nan, 762 ReadSettingGlobal(Settings::values.cpuopt_unsafe_inaccurate_nan,
758 QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true); 763 QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true);
759 ReadSettingGlobal(Settings::values.cpuopt_unsafe_fastmem_check, 764 ReadSettingGlobal(Settings::values.cpuopt_unsafe_fastmem_check,
@@ -807,13 +812,17 @@ void Config::ReadRendererValues() {
807 QStringLiteral("use_asynchronous_gpu_emulation"), true); 812 QStringLiteral("use_asynchronous_gpu_emulation"), true);
808 ReadSettingGlobal(Settings::values.use_nvdec_emulation, QStringLiteral("use_nvdec_emulation"), 813 ReadSettingGlobal(Settings::values.use_nvdec_emulation, QStringLiteral("use_nvdec_emulation"),
809 true); 814 true);
815 ReadSettingGlobal(Settings::values.accelerate_astc, QStringLiteral("accelerate_astc"), true);
810 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);
811 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"), 819 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
812 false); 820 false);
813 ReadSettingGlobal(Settings::values.use_asynchronous_shaders, 821 ReadSettingGlobal(Settings::values.use_asynchronous_shaders,
814 QStringLiteral("use_asynchronous_shaders"), false); 822 QStringLiteral("use_asynchronous_shaders"), false);
815 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"),
816 true); 824 true);
825 ReadSettingGlobal(Settings::values.use_caches_gc, QStringLiteral("use_caches_gc"), false);
817 ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0); 826 ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
818 ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0); 827 ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
819 ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0); 828 ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
@@ -1258,6 +1267,8 @@ void Config::SaveDebuggingValues() {
1258 QString::fromStdString(Settings::values.program_args), QString{}); 1267 QString::fromStdString(Settings::values.program_args), QString{});
1259 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); 1268 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
1260 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); 1269 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
1270 WriteSetting(QStringLiteral("enable_fs_access_log"), Settings::values.enable_fs_access_log,
1271 false);
1261 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); 1272 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
1262 WriteSetting(QStringLiteral("use_debug_asserts"), Settings::values.use_debug_asserts, false); 1273 WriteSetting(QStringLiteral("use_debug_asserts"), Settings::values.use_debug_asserts, false);
1263 WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false); 1274 WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false);
@@ -1334,6 +1345,8 @@ void Config::SaveCpuValues() {
1334 Settings::values.cpuopt_unsafe_unfuse_fma, true); 1345 Settings::values.cpuopt_unsafe_unfuse_fma, true);
1335 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), 1346 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),
1336 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);
1337 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), 1350 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_inaccurate_nan"),
1338 Settings::values.cpuopt_unsafe_inaccurate_nan, true); 1351 Settings::values.cpuopt_unsafe_inaccurate_nan, true);
1339 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_fastmem_check"), 1352 WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_fastmem_check"),
@@ -1388,13 +1401,17 @@ void Config::SaveRendererValues() {
1388 Settings::values.use_asynchronous_gpu_emulation, true); 1401 Settings::values.use_asynchronous_gpu_emulation, true);
1389 WriteSettingGlobal(QStringLiteral("use_nvdec_emulation"), Settings::values.use_nvdec_emulation, 1402 WriteSettingGlobal(QStringLiteral("use_nvdec_emulation"), Settings::values.use_nvdec_emulation,
1390 true); 1403 true);
1404 WriteSettingGlobal(QStringLiteral("accelerate_astc"), Settings::values.accelerate_astc, true);
1391 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);
1392 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"), 1408 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
1393 Settings::values.use_assembly_shaders, false); 1409 Settings::values.use_assembly_shaders, false);
1394 WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"), 1410 WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"),
1395 Settings::values.use_asynchronous_shaders, false); 1411 Settings::values.use_asynchronous_shaders, false);
1396 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,
1397 true); 1413 true);
1414 WriteSettingGlobal(QStringLiteral("use_caches_gc"), Settings::values.use_caches_gc, false);
1398 // 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
1399 WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0); 1416 WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
1400 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_cpu_debug.ui b/src/yuzu/configuration/configure_cpu_debug.ui
index 11ee19a12..c43f89a5a 100644
--- a/src/yuzu/configuration/configure_cpu_debug.ui
+++ b/src/yuzu/configuration/configure_cpu_debug.ui
@@ -34,7 +34,7 @@
34 &lt;br&gt; 34 &lt;br&gt;
35 If you're not sure what these do, keep all of these enabled. 35 If you're not sure what these do, keep all of these enabled.
36 &lt;br&gt; 36 &lt;br&gt;
37 These settings only take effect when CPU Accuracy is "Debug Mode". 37 These settings, when disabled, only take effect when CPU Accuracy is "Debug Mode".
38 &lt;/div&gt; 38 &lt;/div&gt;
39 </string> 39 </string>
40 </property> 40 </property>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index b207e07cb..15d6a5ad7 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -28,17 +28,21 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
28ConfigureDebug::~ConfigureDebug() = default; 28ConfigureDebug::~ConfigureDebug() = default;
29 29
30void ConfigureDebug::SetConfiguration() { 30void ConfigureDebug::SetConfiguration() {
31 ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 31 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
32
33 ui->toggle_console->setEnabled(runtime_lock);
32 ui->toggle_console->setChecked(UISettings::values.show_console); 34 ui->toggle_console->setChecked(UISettings::values.show_console);
33 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); 35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
34 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); 36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
37 ui->fs_access_log->setEnabled(runtime_lock);
38 ui->fs_access_log->setChecked(Settings::values.enable_fs_access_log);
35 ui->reporting_services->setChecked(Settings::values.reporting_services); 39 ui->reporting_services->setChecked(Settings::values.reporting_services);
36 ui->quest_flag->setChecked(Settings::values.quest_flag); 40 ui->quest_flag->setChecked(Settings::values.quest_flag);
37 ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts); 41 ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts);
38 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub); 42 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub);
39 ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 43 ui->enable_graphics_debugging->setEnabled(runtime_lock);
40 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); 44 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
41 ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 45 ui->disable_macro_jit->setEnabled(runtime_lock);
42 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit); 46 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit);
43 ui->extended_logging->setChecked(Settings::values.extended_logging); 47 ui->extended_logging->setChecked(Settings::values.extended_logging);
44} 48}
@@ -47,6 +51,7 @@ void ConfigureDebug::ApplyConfiguration() {
47 UISettings::values.show_console = ui->toggle_console->isChecked(); 51 UISettings::values.show_console = ui->toggle_console->isChecked();
48 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 52 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
49 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 53 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
54 Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked();
50 Settings::values.reporting_services = ui->reporting_services->isChecked(); 55 Settings::values.reporting_services = ui->reporting_services->isChecked();
51 Settings::values.quest_flag = ui->quest_flag->isChecked(); 56 Settings::values.quest_flag = ui->quest_flag->isChecked();
52 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); 57 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index c9e60ee08..c8087542f 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -144,10 +144,17 @@
144 <item> 144 <item>
145 <widget class="QGroupBox" name="groupBox_5"> 145 <widget class="QGroupBox" name="groupBox_5">
146 <property name="title"> 146 <property name="title">
147 <string>Dump</string> 147 <string>Debugging</string>
148 </property> 148 </property>
149 <layout class="QVBoxLayout" name="verticalLayout_7"> 149 <layout class="QVBoxLayout" name="verticalLayout_7">
150 <item> 150 <item>
151 <widget class="QCheckBox" name="fs_access_log">
152 <property name="text">
153 <string>Enable FS Access Log</string>
154 </property>
155 </widget>
156 </item>
157 <item>
151 <widget class="QCheckBox" name="reporting_services"> 158 <widget class="QCheckBox" name="reporting_services">
152 <property name="text"> 159 <property name="text">
153 <string>Enable Verbose Reporting Services</string> 160 <string>Enable Verbose Reporting Services</string>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index fb9ec093c..41a69d9b8 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -70,10 +70,12 @@ void ConfigureGraphics::SetConfiguration() {
70 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); 70 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
71 ui->use_disk_shader_cache->setEnabled(runtime_lock); 71 ui->use_disk_shader_cache->setEnabled(runtime_lock);
72 ui->use_nvdec_emulation->setEnabled(runtime_lock); 72 ui->use_nvdec_emulation->setEnabled(runtime_lock);
73 ui->accelerate_astc->setEnabled(runtime_lock);
73 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); 74 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
74 ui->use_asynchronous_gpu_emulation->setChecked( 75 ui->use_asynchronous_gpu_emulation->setChecked(
75 Settings::values.use_asynchronous_gpu_emulation.GetValue()); 76 Settings::values.use_asynchronous_gpu_emulation.GetValue());
76 ui->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue()); 77 ui->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue());
78 ui->accelerate_astc->setChecked(Settings::values.accelerate_astc.GetValue());
77 79
78 if (Settings::IsConfiguringGlobal()) { 80 if (Settings::IsConfiguringGlobal()) {
79 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); 81 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
@@ -118,6 +120,8 @@ void ConfigureGraphics::ApplyConfiguration() {
118 use_asynchronous_gpu_emulation); 120 use_asynchronous_gpu_emulation);
119 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_nvdec_emulation, 121 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_nvdec_emulation,
120 ui->use_nvdec_emulation, use_nvdec_emulation); 122 ui->use_nvdec_emulation, use_nvdec_emulation);
123 ConfigurationShared::ApplyPerGameSetting(&Settings::values.accelerate_astc, ui->accelerate_astc,
124 accelerate_astc);
121 125
122 if (Settings::IsConfiguringGlobal()) { 126 if (Settings::IsConfiguringGlobal()) {
123 // Guard if during game and set to game-specific value 127 // Guard if during game and set to game-specific value
@@ -254,6 +258,7 @@ void ConfigureGraphics::SetupPerGameUI() {
254 ui->use_asynchronous_gpu_emulation->setEnabled( 258 ui->use_asynchronous_gpu_emulation->setEnabled(
255 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); 259 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
256 ui->use_nvdec_emulation->setEnabled(Settings::values.use_nvdec_emulation.UsingGlobal()); 260 ui->use_nvdec_emulation->setEnabled(Settings::values.use_nvdec_emulation.UsingGlobal());
261 ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal());
257 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); 262 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
258 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); 263 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
259 264
@@ -269,6 +274,8 @@ void ConfigureGraphics::SetupPerGameUI() {
269 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache); 274 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
270 ConfigurationShared::SetColoredTristate( 275 ConfigurationShared::SetColoredTristate(
271 ui->use_nvdec_emulation, Settings::values.use_nvdec_emulation, use_nvdec_emulation); 276 ui->use_nvdec_emulation, Settings::values.use_nvdec_emulation, use_nvdec_emulation);
277 ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc,
278 accelerate_astc);
272 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation, 279 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
273 Settings::values.use_asynchronous_gpu_emulation, 280 Settings::values.use_asynchronous_gpu_emulation,
274 use_asynchronous_gpu_emulation); 281 use_asynchronous_gpu_emulation);
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index c162048a2..6418115cf 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -47,6 +47,7 @@ private:
47 QColor bg_color; 47 QColor bg_color;
48 48
49 ConfigurationShared::CheckState use_nvdec_emulation; 49 ConfigurationShared::CheckState use_nvdec_emulation;
50 ConfigurationShared::CheckState accelerate_astc;
50 ConfigurationShared::CheckState use_disk_shader_cache; 51 ConfigurationShared::CheckState use_disk_shader_cache;
51 ConfigurationShared::CheckState use_asynchronous_gpu_emulation; 52 ConfigurationShared::CheckState use_asynchronous_gpu_emulation;
52 53
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index ab0bd4d77..5b999d84d 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -105,6 +105,13 @@
105 </widget> 105 </widget>
106 </item> 106 </item>
107 <item> 107 <item>
108 <widget class="QCheckBox" name="accelerate_astc">
109 <property name="text">
110 <string>Accelerate ASTC texture decoding</string>
111 </property>
112 </widget>
113 </item>
114 <item>
108 <widget class="QWidget" name="fullscreen_mode_layout" native="true"> 115 <widget class="QWidget" name="fullscreen_mode_layout" native="true">
109 <layout class="QHBoxLayout" name="horizontalLayout_1"> 116 <layout class="QHBoxLayout" name="horizontalLayout_1">
110 <property name="leftMargin"> 117 <property name="leftMargin">
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/game_list.cpp b/src/yuzu/game_list.cpp
index da956c99b..e44907be8 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -521,7 +521,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
521 QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); 521 QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration"));
522 remove_menu->addSeparator(); 522 remove_menu->addSeparator();
523 QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents")); 523 QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
524 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); 524 QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS"));
525 QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS"));
526 QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));
525 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 527 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
526 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 528 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
527 context_menu.addSeparator(); 529 context_menu.addSeparator();
@@ -570,8 +572,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
570 connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { 572 connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() {
571 emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path); 573 emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path);
572 }); 574 });
573 connect(dump_romfs, &QAction::triggered, 575 connect(dump_romfs, &QAction::triggered, [this, program_id, path]() {
574 [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); 576 emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal);
577 });
578 connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() {
579 emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC);
580 });
575 connect(copy_tid, &QAction::triggered, 581 connect(copy_tid, &QAction::triggered,
576 [this, program_id]() { emit CopyTIDRequested(program_id); }); 582 [this, program_id]() { emit CopyTIDRequested(program_id); });
577 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { 583 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index b630e34ff..50402da51 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -45,6 +45,11 @@ enum class GameListRemoveTarget {
45 CustomConfiguration, 45 CustomConfiguration,
46}; 46};
47 47
48enum class DumpRomFSTarget {
49 Normal,
50 SDMC,
51};
52
48enum class InstalledEntryType { 53enum class InstalledEntryType {
49 Game, 54 Game,
50 Update, 55 Update,
@@ -92,7 +97,7 @@ signals:
92 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); 97 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
93 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, 98 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target,
94 const std::string& game_path); 99 const std::string& game_path);
95 void DumpRomFSRequested(u64 program_id, const std::string& game_path); 100 void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
96 void CopyTIDRequested(u64 program_id); 101 void CopyTIDRequested(u64 program_id);
97 void NavigateToGamedbEntryRequested(u64 program_id, 102 void NavigateToGamedbEntryRequested(u64 program_id,
98 const CompatibilityList& compatibility_list); 103 const CompatibilityList& compatibility_list);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index be8933c5c..f462cd072 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -104,6 +104,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
104#include "input_common/main.h" 104#include "input_common/main.h"
105#include "util/overlay_dialog.h" 105#include "util/overlay_dialog.h"
106#include "video_core/gpu.h" 106#include "video_core/gpu.h"
107#include "video_core/renderer_base.h"
107#include "video_core/shader_notify.h" 108#include "video_core/shader_notify.h"
108#include "yuzu/about_dialog.h" 109#include "yuzu/about_dialog.h"
109#include "yuzu/bootmanager.h" 110#include "yuzu/bootmanager.h"
@@ -194,10 +195,10 @@ static void RemoveCachedContents() {
194 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";
195 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";
196 197
197 void(Common::FS::RemoveDirRecursively(offline_fonts)); 198 Common::FS::RemoveDirRecursively(offline_fonts);
198 void(Common::FS::RemoveDirRecursively(offline_manual)); 199 Common::FS::RemoveDirRecursively(offline_manual);
199 void(Common::FS::RemoveDirRecursively(offline_legal_information)); 200 Common::FS::RemoveDirRecursively(offline_legal_information);
200 void(Common::FS::RemoveDirRecursively(offline_system_data)); 201 Common::FS::RemoveDirRecursively(offline_system_data);
201} 202}
202 203
203GMainWindow::GMainWindow() 204GMainWindow::GMainWindow()
@@ -236,7 +237,8 @@ GMainWindow::GMainWindow()
236 const auto build_id = std::string(Common::g_build_id); 237 const auto build_id = std::string(Common::g_build_id);
237 238
238 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);
239 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);
240 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;
241 243
242 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); 244 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);
@@ -1025,7 +1027,11 @@ void GMainWindow::InitializeHotkeys() {
1025 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this), 1027 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this),
1026 &QShortcut::activated, this, 1028 &QShortcut::activated, this,
1027 [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); 1029 [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
1028 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 });
1029 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this), 1035 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this),
1030 &QShortcut::activated, this, [&] { 1036 &QShortcut::activated, this, [&] {
1031 Settings::values.mouse_panning = !Settings::values.mouse_panning; 1037 Settings::values.mouse_panning = !Settings::values.mouse_panning;
@@ -1417,8 +1423,12 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
1417 title_name = Common::FS::PathToUTF8String( 1423 title_name = Common::FS::PathToUTF8String(
1418 std::filesystem::path{filename.toStdU16String()}.filename()); 1424 std::filesystem::path{filename.toStdU16String()}.filename());
1419 } 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;
1420 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);
1421 UpdateWindowTitle(title_name, title_version); 1430 const auto gpu_vendor = system.GPU().Renderer().GetDeviceVendor();
1431 UpdateWindowTitle(title_name, title_version, gpu_vendor);
1422 1432
1423 loading_screen->Prepare(system.GetAppLoader()); 1433 loading_screen->Prepare(system.GetAppLoader());
1424 loading_screen->show(); 1434 loading_screen->show();
@@ -1739,8 +1749,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
1739 RemoveAddOnContent(program_id, entry_type); 1749 RemoveAddOnContent(program_id, entry_type);
1740 break; 1750 break;
1741 } 1751 }
1742 void(Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / 1752 Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
1743 "game_list")); 1753 "game_list");
1744 game_list->PopulateAsync(UISettings::values.game_dirs); 1754 game_list->PopulateAsync(UISettings::values.game_dirs);
1745} 1755}
1746 1756
@@ -1872,7 +1882,8 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& g
1872 } 1882 }
1873} 1883}
1874 1884
1875void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 1885void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path,
1886 DumpRomFSTarget target) {
1876 const auto failed = [this] { 1887 const auto failed = [this] {
1877 QMessageBox::warning(this, tr("RomFS Extraction Failed!"), 1888 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
1878 tr("There was an error copying the RomFS files or the user " 1889 tr("There was an error copying the RomFS files or the user "
@@ -1900,7 +1911,10 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1900 return; 1911 return;
1901 } 1912 }
1902 1913
1903 const auto dump_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir); 1914 const auto dump_dir =
1915 target == DumpRomFSTarget::Normal
1916 ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)
1917 : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents";
1904 const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); 1918 const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id);
1905 1919
1906 const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); 1920 const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir);
@@ -1910,7 +1924,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1910 if (*romfs_title_id == program_id) { 1924 if (*romfs_title_id == program_id) {
1911 const u64 ivfc_offset = loader->ReadRomFSIVFCOffset(); 1925 const u64 ivfc_offset = loader->ReadRomFSIVFCOffset();
1912 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), installed}; 1926 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), installed};
1913 romfs = pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program); 1927 romfs =
1928 pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program, nullptr, false);
1914 } else { 1929 } else {
1915 romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS(); 1930 romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
1916 } 1931 }
@@ -2209,8 +2224,8 @@ void GMainWindow::OnMenuInstallToNAND() {
2209 : tr("%n file(s) failed to install\n", "", failed_files.size())); 2224 : tr("%n file(s) failed to install\n", "", failed_files.size()));
2210 2225
2211 QMessageBox::information(this, tr("Install Results"), install_results); 2226 QMessageBox::information(this, tr("Install Results"), install_results);
2212 void(Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / 2227 Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
2213 "game_list")); 2228 "game_list");
2214 game_list->PopulateAsync(UISettings::values.game_dirs); 2229 game_list->PopulateAsync(UISettings::values.game_dirs);
2215 ui.action_Install_File_NAND->setEnabled(true); 2230 ui.action_Install_File_NAND->setEnabled(true);
2216} 2231}
@@ -2842,25 +2857,27 @@ void GMainWindow::MigrateConfigFiles() {
2842 LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination); 2857 LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination);
2843 if (!Common::FS::RenameFile(origin, destination)) { 2858 if (!Common::FS::RenameFile(origin, destination)) {
2844 // Delete the old config file if one already exists in the new location. 2859 // Delete the old config file if one already exists in the new location.
2845 void(Common::FS::RemoveFile(origin)); 2860 Common::FS::RemoveFile(origin);
2846 } 2861 }
2847 } 2862 }
2848} 2863}
2849 2864
2850void GMainWindow::UpdateWindowTitle(const std::string& title_name, 2865void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_view title_version,
2851 const std::string& title_version) { 2866 std::string_view gpu_vendor) {
2852 const auto branch_name = std::string(Common::g_scm_branch); 2867 const auto branch_name = std::string(Common::g_scm_branch);
2853 const auto description = std::string(Common::g_scm_desc); 2868 const auto description = std::string(Common::g_scm_desc);
2854 const auto build_id = std::string(Common::g_build_id); 2869 const auto build_id = std::string(Common::g_build_id);
2855 2870
2856 const auto yuzu_title = fmt::format("yuzu | {}-{}", branch_name, description); 2871 const auto yuzu_title = fmt::format("yuzu | {}-{}", branch_name, description);
2857 const auto override_title = fmt::format(std::string(Common::g_title_bar_format_idle), build_id); 2872 const auto override_title =
2873 fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
2858 const auto window_title = override_title.empty() ? yuzu_title : override_title; 2874 const auto window_title = override_title.empty() ? yuzu_title : override_title;
2859 2875
2860 if (title_name.empty()) { 2876 if (title_name.empty()) {
2861 setWindowTitle(QString::fromStdString(window_title)); 2877 setWindowTitle(QString::fromStdString(window_title));
2862 } else { 2878 } else {
2863 const auto run_title = fmt::format("{} | {} | {}", window_title, title_name, title_version); 2879 const auto run_title =
2880 fmt::format("{} | {} | {} | {}", window_title, title_name, title_version, gpu_vendor);
2864 setWindowTitle(QString::fromStdString(run_title)); 2881 setWindowTitle(QString::fromStdString(run_title));
2865 } 2882 }
2866} 2883}
@@ -3036,9 +3053,9 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
3036 3053
3037 const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); 3054 const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
3038 3055
3039 void(Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated")); 3056 Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated");
3040 void(Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated")); 3057 Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated");
3041 void(Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated")); 3058 Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated");
3042 } 3059 }
3043 3060
3044 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); 3061 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 11f152cbe..45c8310e1 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -34,6 +34,7 @@ class QProgressDialog;
34class WaitTreeWidget; 34class WaitTreeWidget;
35enum class GameListOpenTarget; 35enum class GameListOpenTarget;
36enum class GameListRemoveTarget; 36enum class GameListRemoveTarget;
37enum class DumpRomFSTarget;
37enum class InstalledEntryType; 38enum class InstalledEntryType;
38class GameListPlaceholder; 39class GameListPlaceholder;
39 40
@@ -244,7 +245,7 @@ private slots:
244 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); 245 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
245 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, 246 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
246 const std::string& game_path); 247 const std::string& game_path);
247 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); 248 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
248 void OnGameListCopyTID(u64 program_id); 249 void OnGameListCopyTID(u64 program_id);
249 void OnGameListNavigateToGamedbEntry(u64 program_id, 250 void OnGameListNavigateToGamedbEntry(u64 program_id,
250 const CompatibilityList& compatibility_list); 251 const CompatibilityList& compatibility_list);
@@ -287,8 +288,8 @@ private:
287 InstallResult InstallNSPXCI(const QString& filename); 288 InstallResult InstallNSPXCI(const QString& filename);
288 InstallResult InstallNCA(const QString& filename); 289 InstallResult InstallNCA(const QString& filename);
289 void MigrateConfigFiles(); 290 void MigrateConfigFiles();
290 void UpdateWindowTitle(const std::string& title_name = {}, 291 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
291 const std::string& title_version = {}); 292 std::string_view gpu_vendor = {});
292 void UpdateStatusBar(); 293 void UpdateStatusBar();
293 void UpdateStatusButtons(); 294 void UpdateStatusButtons();
294 void UpdateUISettings(); 295 void UpdateUISettings();
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 63f368fe5..60bf66ec0 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -443,12 +443,16 @@ 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(
449 sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false)); 451 sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
450 Settings::values.use_asynchronous_shaders.SetValue( 452 Settings::values.use_nvdec_emulation.SetValue(
451 sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false)); 453 sdl2_config->GetBoolean("Renderer", "use_nvdec_emulation", true));
454 Settings::values.accelerate_astc.SetValue(
455 sdl2_config->GetBoolean("Renderer", "accelerate_astc", true));
452 Settings::values.use_fast_gpu_time.SetValue( 456 Settings::values.use_fast_gpu_time.SetValue(
453 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true)); 457 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
454 458
@@ -477,6 +481,8 @@ void Config::ReadValues() {
477 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); 481 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
478 Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); 482 Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false);
479 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); 483 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
484 Settings::values.enable_fs_access_log =
485 sdl2_config->GetBoolean("Debugging", "enable_fs_access_log", false);
480 Settings::values.reporting_services = 486 Settings::values.reporting_services =
481 sdl2_config->GetBoolean("Debugging", "reporting_services", false); 487 sdl2_config->GetBoolean("Debugging", "reporting_services", false);
482 Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false); 488 Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index f48d935a1..cc9850aad 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -194,6 +194,14 @@ use_assembly_shaders =
194# 0 (default): Off, 1: On 194# 0 (default): Off, 1: On
195use_asynchronous_shaders = 195use_asynchronous_shaders =
196 196
197# Enable NVDEC emulation.
198# 0: Off, 1 (default): On
199use_nvdec_emulation =
200
201# Accelerate ASTC texture decoding.
202# 0: Off, 1 (default): On
203accelerate_astc =
204
197# Turns on the frame limiter, which will limit frames output to the target game speed 205# Turns on the frame limiter, which will limit frames output to the target game speed
198# 0: Off, 1: On (default) 206# 0: Off, 1: On (default)
199use_frame_limit = 207use_frame_limit =
@@ -219,6 +227,10 @@ use_asynchronous_gpu_emulation =
219# 0: Off, 1 (default): On 227# 0: Off, 1 (default): On
220use_vsync = 228use_vsync =
221 229
230# Whether to use garbage collection or not for GPU caches.
231# 0 (default): Off, 1: On
232use_caches_gc =
233
222# 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.
223# 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.
224bg_red = 236bg_red =
@@ -252,7 +264,10 @@ swap_screen =
252 264
253[Audio] 265[Audio]
254# Which audio output engine to use. 266# Which audio output engine to use.
255# auto (default): Auto-select, null: No audio output, cubeb: Cubeb audio engine (if available) 267# auto (default): Auto-select
268# cubeb: Cubeb audio engine (if available)
269# sdl2: SDL2 audio engine (if available)
270# null: No audio output
256output_engine = 271output_engine =
257 272
258# Whether or not to enable the audio-stretching post-processing effect. 273# Whether or not to enable the audio-stretching post-processing effect.
@@ -338,6 +353,8 @@ record_frame_times =
338dump_exefs=false 353dump_exefs=false
339# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them 354# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
340dump_nso=false 355dump_nso=false
356# Determines whether or not yuzu will save the filesystem access log.
357enable_fs_access_log=false
341# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode 358# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
342# false: Retail/Normal Mode (default), true: Kiosk Mode 359# false: Retail/Normal Mode (default), true: Kiosk Mode
343quest_flag = 360quest_flag =
@@ -349,6 +366,9 @@ use_debug_asserts =
349use_auto_stub = 366use_auto_stub =
350# Enables/Disables the macro JIT compiler 367# Enables/Disables the macro JIT compiler
351disable_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
352 372
353[WebService] 373[WebService]
354# 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());