summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules6
-rw-r--r--CMakeLists.txt2
-rw-r--r--externals/CMakeLists.txt9
m---------externals/sirit0
m---------externals/zstd0
-rw-r--r--src/common/CMakeLists.txt5
-rw-r--r--src/common/assert.h18
-rw-r--r--src/common/bit_util.h16
-rw-r--r--src/common/lz4_compression.cpp76
-rw-r--r--src/common/lz4_compression.h57
-rw-r--r--src/common/multi_level_queue.h2
-rw-r--r--src/common/scope_exit.h2
-rw-r--r--src/common/swap.h96
-rw-r--r--src/common/zstd_compression.cpp53
-rw-r--r--src/common/zstd_compression.h44
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp20
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h20
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp42
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h16
-rw-r--r--src/core/core.cpp26
-rw-r--r--src/core/core.h14
-rw-r--r--src/core/core_cpu.cpp6
-rw-r--r--src/core/crypto/key_manager.cpp3
-rw-r--r--src/core/file_sys/control_metadata.cpp6
-rw-r--r--src/core/file_sys/control_metadata.h30
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp4
-rw-r--r--src/core/file_sys/nca_metadata.cpp8
-rw-r--r--src/core/file_sys/nca_metadata.h3
-rw-r--r--src/core/file_sys/patch_manager.cpp22
-rw-r--r--src/core/file_sys/patch_manager.h2
-rw-r--r--src/core/file_sys/program_metadata.cpp27
-rw-r--r--src/core/file_sys/program_metadata.h2
-rw-r--r--src/core/file_sys/registered_cache.cpp275
-rw-r--r--src/core/file_sys/registered_cache.h156
-rw-r--r--src/core/file_sys/romfs_factory.cpp2
-rw-r--r--src/core/file_sys/savedata_factory.cpp8
-rw-r--r--src/core/file_sys/savedata_factory.h11
-rw-r--r--src/core/file_sys/submission_package.cpp13
-rw-r--r--src/core/file_sys/submission_package.h11
-rw-r--r--src/core/frontend/emu_window.h39
-rw-r--r--src/core/gdbstub/gdbstub.cpp2
-rw-r--r--src/core/hle/ipc_helpers.h4
-rw-r--r--src/core/hle/kernel/client_port.cpp10
-rw-r--r--src/core/hle/kernel/client_port.h2
-rw-r--r--src/core/hle/kernel/client_session.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp2
-rw-r--r--src/core/hle/kernel/object.cpp1
-rw-r--r--src/core/hle/kernel/object.h1
-rw-r--r--src/core/hle/kernel/process.cpp5
-rw-r--r--src/core/hle/kernel/process.h2
-rw-r--r--src/core/hle/kernel/readable_event.h2
-rw-r--r--src/core/hle/kernel/resource_limit.h2
-rw-r--r--src/core/hle/kernel/scheduler.cpp1
-rw-r--r--src/core/hle/kernel/server_port.cpp7
-rw-r--r--src/core/hle/kernel/server_port.h9
-rw-r--r--src/core/hle/kernel/server_session.cpp11
-rw-r--r--src/core/hle/kernel/server_session.h9
-rw-r--r--src/core/hle/kernel/shared_memory.h2
-rw-r--r--src/core/hle/kernel/svc.cpp491
-rw-r--r--src/core/hle/kernel/svc.h6
-rw-r--r--src/core/hle/kernel/svc_wrap.h359
-rw-r--r--src/core/hle/kernel/thread.cpp5
-rw-r--r--src/core/hle/kernel/thread.h8
-rw-r--r--src/core/hle/kernel/transfer_memory.cpp22
-rw-r--r--src/core/hle/kernel/transfer_memory.h20
-rw-r--r--src/core/hle/kernel/vm_manager.cpp80
-rw-r--r--src/core/hle/kernel/vm_manager.h49
-rw-r--r--src/core/hle/kernel/writable_event.h2
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/acc/acc.cpp4
-rw-r--r--src/core/hle/service/acc/acc_su.cpp6
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp8
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp6
-rw-r--r--src/core/hle/service/am/am.cpp35
-rw-r--r--src/core/hle/service/am/applet_ae.cpp8
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp2
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp8
-rw-r--r--src/core/hle/service/apm/interface.cpp2
-rw-r--r--src/core/hle/service/audio/audin_u.cpp5
-rw-r--r--src/core/hle/service/audio/audout_u.cpp9
-rw-r--r--src/core/hle/service/audio/audrec_u.cpp4
-rw-r--r--src/core/hle/service/audio/audren_u.cpp13
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp3
-rw-r--r--src/core/hle/service/caps/caps.cpp89
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp30
-rw-r--r--src/core/hle/service/filesystem/filesystem.h4
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp111
-rw-r--r--src/core/hle/service/friend/friend.cpp5
-rw-r--r--src/core/hle/service/hid/hid.cpp15
-rw-r--r--src/core/hle/service/ldn/ldn.cpp54
-rw-r--r--src/core/hle/service/ldr/ldr.cpp151
-rw-r--r--src/core/hle/service/nifm/nifm.cpp4
-rw-r--r--src/core/hle/service/npns/npns.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h2
-rw-r--r--src/core/hle/service/nvdrv/interface.h2
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.h2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp16
-rw-r--r--src/core/hle/service/pctl/module.cpp7
-rw-r--r--src/core/hle/service/pm/pm.cpp21
-rw-r--r--src/core/hle/service/service.h2
-rw-r--r--src/core/hle/service/set/set.cpp4
-rw-r--r--src/core/hle/service/set/set_cal.cpp2
-rw-r--r--src/core/hle/service/set/set_cal.h2
-rw-r--r--src/core/hle/service/set/set_sys.cpp27
-rw-r--r--src/core/hle/service/sockets/bsd.cpp5
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp12
-rw-r--r--src/core/hle/service/spl/module.cpp4
-rw-r--r--src/core/hle/service/ssl/ssl.cpp12
-rw-r--r--src/core/hle/service/time/interface.cpp10
-rw-r--r--src/core/hle/service/vi/vi.cpp5
-rw-r--r--src/core/loader/nso.cpp21
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/memory.cpp4
-rw-r--r--src/core/memory.h13
-rw-r--r--src/core/settings.h1
-rw-r--r--src/tests/core/arm/arm_test_common.cpp3
-rw-r--r--src/video_core/CMakeLists.txt11
-rw-r--r--src/video_core/dma_pusher.cpp1
-rw-r--r--src/video_core/engines/fermi_2d.cpp3
-rw-r--r--src/video_core/engines/fermi_2d.h8
-rw-r--r--src/video_core/engines/kepler_compute.h10
-rw-r--r--src/video_core/engines/kepler_memory.cpp4
-rw-r--r--src/video_core/engines/kepler_memory.h7
-rw-r--r--src/video_core/engines/maxwell_3d.cpp19
-rw-r--r--src/video_core/engines/maxwell_3d.h24
-rw-r--r--src/video_core/engines/maxwell_dma.cpp4
-rw-r--r--src/video_core/engines/maxwell_dma.h9
-rw-r--r--src/video_core/engines/shader_bytecode.h11
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/gpu_asynch.cpp2
-rw-r--r--src/video_core/gpu_thread.cpp43
-rw-r--r--src/video_core/gpu_thread.h71
-rw-r--r--src/video_core/macro_interpreter.cpp20
-rw-r--r--src/video_core/memory_manager.cpp138
-rw-r--r--src/video_core/memory_manager.h16
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.cpp44
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.h16
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp63
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h13
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp44
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp208
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h16
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp59
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp51
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h73
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp610
-rw-r--r--src/video_core/renderer_opengl/gl_state.h52
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp1
-rw-r--r--src/video_core/renderer_opengl/utils.cpp28
-rw-r--r--src/video_core/renderer_opengl/utils.h20
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp1381
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.h80
-rw-r--r--src/video_core/shader/decode/memory.cpp109
-rw-r--r--src/video_core/shader/decode/texture.cpp114
-rw-r--r--src/video_core/shader/decode/xmad.cpp39
-rw-r--r--src/video_core/shader/shader_ir.h28
-rw-r--r--src/video_core/shader/track.cpp17
-rw-r--r--src/video_core/surface.cpp2
-rw-r--r--src/video_core/texture_cache.cpp386
-rw-r--r--src/video_core/texture_cache.h586
-rw-r--r--src/video_core/textures/convert.cpp1
-rw-r--r--src/video_core/textures/convert.h5
-rw-r--r--src/video_core/textures/texture.h2
-rw-r--r--src/yuzu/CMakeLists.txt14
-rw-r--r--src/yuzu/applets/profile_select.cpp1
-rw-r--r--src/yuzu/applets/profile_select.h2
-rw-r--r--src/yuzu/bootmanager.cpp208
-rw-r--r--src/yuzu/bootmanager.h29
-rw-r--r--src/yuzu/configuration/config.cpp69
-rw-r--r--src/yuzu/configuration/config.h3
-rw-r--r--src/yuzu/configuration/configure.ui19
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp16
-rw-r--r--src/yuzu/configuration/configure_dialog.h3
-rw-r--r--src/yuzu/configuration/configure_general.cpp8
-rw-r--r--src/yuzu/configuration/configure_general.h1
-rw-r--r--src/yuzu/configuration/configure_general.ui24
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp3
-rw-r--r--src/yuzu/configuration/configure_graphics.ui7
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp122
-rw-r--r--src/yuzu/configuration/configure_hotkeys.h47
-rw-r--r--src/yuzu/configuration/configure_hotkeys.ui42
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp460
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.h96
-rw-r--r--src/yuzu/game_list.cpp18
-rw-r--r--src/yuzu/game_list.h8
-rw-r--r--src/yuzu/game_list_p.h2
-rw-r--r--src/yuzu/game_list_worker.cpp125
-rw-r--r--src/yuzu/game_list_worker.h16
-rw-r--r--src/yuzu/hotkeys.cpp73
-rw-r--r--src/yuzu/hotkeys.h42
-rw-r--r--src/yuzu/hotkeys.ui46
-rw-r--r--src/yuzu/loading_screen.cpp7
-rw-r--r--src/yuzu/main.cpp169
-rw-r--r--src/yuzu/main.h15
-rw-r--r--src/yuzu/ui_settings.cpp1
-rw-r--r--src/yuzu/ui_settings.h11
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.cpp37
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.h24
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp37
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h2
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
211 files changed, 6511 insertions, 2815 deletions
diff --git a/.gitmodules b/.gitmodules
index 2558a5ebc..3a49c4874 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -40,3 +40,9 @@
40[submodule "Vulkan-Headers"] 40[submodule "Vulkan-Headers"]
41 path = externals/Vulkan-Headers 41 path = externals/Vulkan-Headers
42 url = https://github.com/KhronosGroup/Vulkan-Headers.git 42 url = https://github.com/KhronosGroup/Vulkan-Headers.git
43[submodule "externals/zstd"]
44 path = externals/zstd
45 url = https://github.com/facebook/zstd
46[submodule "sirit"]
47 path = externals/sirit
48 url = https://github.com/ReinUsesLisp/sirit
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ab18275d3..6a417017c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -309,7 +309,7 @@ if (CLANG_FORMAT)
309 set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") 309 set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
310 if (WIN32) 310 if (WIN32)
311 add_custom_target(clang-format 311 add_custom_target(clang-format
312 COMMAND powershell.exe -Command "Get-ChildItem ${SRCS}/* -Include *.cpp,*.h -Recurse | Foreach {${CLANG_FORMAT} -i $_.fullname}" 312 COMMAND powershell.exe -Command "Get-ChildItem '${SRCS}/*' -Include *.cpp,*.h -Recurse | Foreach {&'${CLANG_FORMAT}' -i $_.fullname}"
313 COMMENT ${CCOMMENT}) 313 COMMENT ${CCOMMENT})
314 elseif(MINGW) 314 elseif(MINGW)
315 add_custom_target(clang-format 315 add_custom_target(clang-format
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index e156bbece..3f8b6cda8 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -49,6 +49,10 @@ add_subdirectory(open_source_archives EXCLUDE_FROM_ALL)
49add_library(unicorn-headers INTERFACE) 49add_library(unicorn-headers INTERFACE)
50target_include_directories(unicorn-headers INTERFACE ./unicorn/include) 50target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
51 51
52# Zstandard
53add_subdirectory(zstd/build/cmake EXCLUDE_FROM_ALL)
54target_include_directories(libzstd_static INTERFACE ./zstd/lib)
55
52# SoundTouch 56# SoundTouch
53add_subdirectory(soundtouch) 57add_subdirectory(soundtouch)
54 58
@@ -68,6 +72,11 @@ if (USE_DISCORD_PRESENCE)
68 target_include_directories(discord-rpc INTERFACE ./discord-rpc/include) 72 target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
69endif() 73endif()
70 74
75# Sirit
76if (ENABLE_VULKAN)
77 add_subdirectory(sirit)
78endif()
79
71if (ENABLE_WEB_SERVICE) 80if (ENABLE_WEB_SERVICE)
72 # LibreSSL 81 # LibreSSL
73 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "") 82 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
diff --git a/externals/sirit b/externals/sirit
new file mode 160000
Subproject f7c4b07a7e14edb1dcd93bc9879c823423705c2
diff --git a/externals/zstd b/externals/zstd
new file mode 160000
Subproject 470344d33e1d52a2ada75d278466da8d4ee2faf
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 850ce8006..1e8e1b215 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -91,6 +91,8 @@ add_library(common STATIC
91 logging/log.h 91 logging/log.h
92 logging/text_formatter.cpp 92 logging/text_formatter.cpp
93 logging/text_formatter.h 93 logging/text_formatter.h
94 lz4_compression.cpp
95 lz4_compression.h
94 math_util.h 96 math_util.h
95 memory_hook.cpp 97 memory_hook.cpp
96 memory_hook.h 98 memory_hook.h
@@ -123,6 +125,8 @@ add_library(common STATIC
123 uint128.h 125 uint128.h
124 vector_math.h 126 vector_math.h
125 web_result.h 127 web_result.h
128 zstd_compression.cpp
129 zstd_compression.h
126) 130)
127 131
128if(ARCHITECTURE_x86_64) 132if(ARCHITECTURE_x86_64)
@@ -136,3 +140,4 @@ endif()
136create_target_directory_groups(common) 140create_target_directory_groups(common)
137 141
138target_link_libraries(common PUBLIC Boost::boost fmt microprofile) 142target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
143target_link_libraries(common PRIVATE lz4_static libzstd_static)
diff --git a/src/common/assert.h b/src/common/assert.h
index 6002f7ab1..4b0e3f64e 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -57,3 +57,21 @@ __declspec(noinline, noreturn)
57 57
58#define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!") 58#define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!")
59#define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__) 59#define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__)
60
61// If the assert is ignored, execute _b_
62#define ASSERT_OR_EXECUTE(_a_, _b_) \
63 do { \
64 ASSERT(_a_); \
65 if (!(_a_)) { \
66 _b_ \
67 } \
68 } while (0)
69
70// If the assert is ignored, execute _b_
71#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \
72 do { \
73 ASSERT_MSG(_a_, __VA_ARGS__); \
74 if (!(_a_)) { \
75 _b_ \
76 } \
77 } while (0)
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index a4f9ed4aa..d032df413 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
32 return 32; 32 return 32;
33} 33}
34 34
35inline u64 CountLeadingZeroes64(u64 value) { 35inline u32 CountLeadingZeroes64(u64 value) {
36 unsigned long leading_zero = 0; 36 unsigned long leading_zero = 0;
37 37
38 if (_BitScanReverse64(&leading_zero, value) != 0) { 38 if (_BitScanReverse64(&leading_zero, value) != 0) {
@@ -47,15 +47,15 @@ inline u32 CountLeadingZeroes32(u32 value) {
47 return 32; 47 return 32;
48 } 48 }
49 49
50 return __builtin_clz(value); 50 return static_cast<u32>(__builtin_clz(value));
51} 51}
52 52
53inline u64 CountLeadingZeroes64(u64 value) { 53inline u32 CountLeadingZeroes64(u64 value) {
54 if (value == 0) { 54 if (value == 0) {
55 return 64; 55 return 64;
56 } 56 }
57 57
58 return __builtin_clzll(value); 58 return static_cast<u32>(__builtin_clzll(value));
59} 59}
60#endif 60#endif
61 61
@@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
70 return 32; 70 return 32;
71} 71}
72 72
73inline u64 CountTrailingZeroes64(u64 value) { 73inline u32 CountTrailingZeroes64(u64 value) {
74 unsigned long trailing_zero = 0; 74 unsigned long trailing_zero = 0;
75 75
76 if (_BitScanForward64(&trailing_zero, value) != 0) { 76 if (_BitScanForward64(&trailing_zero, value) != 0) {
@@ -85,15 +85,15 @@ inline u32 CountTrailingZeroes32(u32 value) {
85 return 32; 85 return 32;
86 } 86 }
87 87
88 return __builtin_ctz(value); 88 return static_cast<u32>(__builtin_ctz(value));
89} 89}
90 90
91inline u64 CountTrailingZeroes64(u64 value) { 91inline u32 CountTrailingZeroes64(u64 value) {
92 if (value == 0) { 92 if (value == 0) {
93 return 64; 93 return 64;
94 } 94 }
95 95
96 return __builtin_ctzll(value); 96 return static_cast<u32>(__builtin_ctzll(value));
97} 97}
98#endif 98#endif
99 99
diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp
new file mode 100644
index 000000000..ade6759bb
--- /dev/null
+++ b/src/common/lz4_compression.cpp
@@ -0,0 +1,76 @@
1// Copyright 2019 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 <lz4hc.h>
7
8#include "common/assert.h"
9#include "common/lz4_compression.h"
10
11namespace Common::Compression {
12
13std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size) {
14 ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
15
16 const auto source_size_int = static_cast<int>(source_size);
17 const int max_compressed_size = LZ4_compressBound(source_size_int);
18 std::vector<u8> compressed(max_compressed_size);
19
20 const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source),
21 reinterpret_cast<char*>(compressed.data()),
22 source_size_int, max_compressed_size);
23
24 if (compressed_size <= 0) {
25 // Compression failed
26 return {};
27 }
28
29 compressed.resize(compressed_size);
30
31 return compressed;
32}
33
34std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
35 s32 compression_level) {
36 ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
37
38 compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
39
40 const auto source_size_int = static_cast<int>(source_size);
41 const int max_compressed_size = LZ4_compressBound(source_size_int);
42 std::vector<u8> compressed(max_compressed_size);
43
44 const int compressed_size = LZ4_compress_HC(
45 reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
46 source_size_int, max_compressed_size, compression_level);
47
48 if (compressed_size <= 0) {
49 // Compression failed
50 return {};
51 }
52
53 compressed.resize(compressed_size);
54
55 return compressed;
56}
57
58std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size) {
59 return CompressDataLZ4HC(source, source_size, LZ4HC_CLEVEL_MAX);
60}
61
62std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
63 std::size_t uncompressed_size) {
64 std::vector<u8> uncompressed(uncompressed_size);
65 const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()),
66 reinterpret_cast<char*>(uncompressed.data()),
67 static_cast<int>(compressed.size()),
68 static_cast<int>(uncompressed.size()));
69 if (static_cast<int>(uncompressed_size) != size_check) {
70 // Decompression failed
71 return {};
72 }
73 return uncompressed;
74}
75
76} // namespace Common::Compression
diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h
new file mode 100644
index 000000000..4c16f6e03
--- /dev/null
+++ b/src/common/lz4_compression.h
@@ -0,0 +1,57 @@
1// Copyright 2019 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 <vector>
8
9#include "common/common_types.h"
10
11namespace Common::Compression {
12
13/**
14 * Compresses a source memory region with LZ4 and returns the compressed data in a vector.
15 *
16 * @param source the uncompressed source memory region.
17 * @param source_size the size in bytes of the uncompressed source memory region.
18 *
19 * @return the compressed data.
20 */
21std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
22
23/**
24 * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression
25 * levels result in a smaller compressed size, but require more CPU time for compression. The
26 * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can
27 * also be decompressed with the default LZ4 decompression.
28 *
29 * @param source the uncompressed source memory region.
30 * @param source_size the size in bytes of the uncompressed source memory region.
31 * @param compression_level the used compression level. Should be between 3 and 12.
32 *
33 * @return the compressed data.
34 */
35std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level);
36
37/**
38 * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
39 *
40 * @param source the uncompressed source memory region.
41 * @param source_size the size in bytes of the uncompressed source memory region.
42 *
43 * @return the compressed data.
44 */
45std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
46
47/**
48 * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector.
49 *
50 * @param compressed the compressed source memory region.
51 * @param uncompressed_size the size in bytes of the uncompressed data.
52 *
53 * @return the decompressed data.
54 */
55std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size);
56
57} // namespace Common::Compression \ No newline at end of file
diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h
index 2b61b91e0..9cb448f56 100644
--- a/src/common/multi_level_queue.h
+++ b/src/common/multi_level_queue.h
@@ -72,7 +72,7 @@ public:
72 u64 prios = mlq.used_priorities; 72 u64 prios = mlq.used_priorities;
73 prios &= ~((1ULL << (current_priority + 1)) - 1); 73 prios &= ~((1ULL << (current_priority + 1)) - 1);
74 if (prios == 0) { 74 if (prios == 0) {
75 current_priority = mlq.depth(); 75 current_priority = static_cast<u32>(mlq.depth());
76 } else { 76 } else {
77 current_priority = CountTrailingZeroes64(prios); 77 current_priority = CountTrailingZeroes64(prios);
78 it = GetBeginItForPrio(); 78 it = GetBeginItForPrio();
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
index baf1f1c9e..1176a72b1 100644
--- a/src/common/scope_exit.h
+++ b/src/common/scope_exit.h
@@ -20,7 +20,7 @@ struct ScopeExitHelper {
20 20
21template <typename Func> 21template <typename Func>
22ScopeExitHelper<Func> ScopeExit(Func&& func) { 22ScopeExitHelper<Func> ScopeExit(Func&& func) {
23 return ScopeExitHelper<Func>(std::move(func)); 23 return ScopeExitHelper<Func>(std::forward<Func>(func));
24} 24}
25} // namespace detail 25} // namespace detail
26 26
diff --git a/src/common/swap.h b/src/common/swap.h
index b3eab1324..71932c2bb 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -21,11 +21,6 @@
21 21
22#if defined(_MSC_VER) 22#if defined(_MSC_VER)
23#include <cstdlib> 23#include <cstdlib>
24#elif defined(__linux__)
25#include <byteswap.h>
26#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
27 defined(__NetBSD__) || defined(__OpenBSD__)
28#include <sys/endian.h>
29#endif 24#endif
30#include <cstring> 25#include <cstring>
31#include "common/common_types.h" 26#include "common/common_types.h"
@@ -62,86 +57,49 @@
62namespace Common { 57namespace Common {
63 58
64#ifdef _MSC_VER 59#ifdef _MSC_VER
65inline u16 swap16(u16 _data) { 60[[nodiscard]] inline u16 swap16(u16 data) noexcept {
66 return _byteswap_ushort(_data); 61 return _byteswap_ushort(data);
67} 62}
68inline u32 swap32(u32 _data) { 63[[nodiscard]] inline u32 swap32(u32 data) noexcept {
69 return _byteswap_ulong(_data); 64 return _byteswap_ulong(data);
70} 65}
71inline u64 swap64(u64 _data) { 66[[nodiscard]] inline u64 swap64(u64 data) noexcept {
72 return _byteswap_uint64(_data); 67 return _byteswap_uint64(data);
73} 68}
74#elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6) 69#elif defined(__clang__) || defined(__GNUC__)
75inline u16 swap16(u16 _data) { 70#if defined(__Bitrig__) || defined(__OpenBSD__)
76 u32 data = _data;
77 __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data));
78 return (u16)data;
79}
80inline u32 swap32(u32 _data) {
81 __asm__("rev %0, %1\n" : "=l"(_data) : "l"(_data));
82 return _data;
83}
84inline u64 swap64(u64 _data) {
85 return ((u64)swap32(_data) << 32) | swap32(_data >> 32);
86}
87#elif __linux__
88inline u16 swap16(u16 _data) {
89 return bswap_16(_data);
90}
91inline u32 swap32(u32 _data) {
92 return bswap_32(_data);
93}
94inline u64 swap64(u64 _data) {
95 return bswap_64(_data);
96}
97#elif __APPLE__
98inline __attribute__((always_inline)) u16 swap16(u16 _data) {
99 return (_data >> 8) | (_data << 8);
100}
101inline __attribute__((always_inline)) u32 swap32(u32 _data) {
102 return __builtin_bswap32(_data);
103}
104inline __attribute__((always_inline)) u64 swap64(u64 _data) {
105 return __builtin_bswap64(_data);
106}
107#elif defined(__Bitrig__) || defined(__OpenBSD__)
108// redefine swap16, swap32, swap64 as inline functions 71// redefine swap16, swap32, swap64 as inline functions
109#undef swap16 72#undef swap16
110#undef swap32 73#undef swap32
111#undef swap64 74#undef swap64
112inline u16 swap16(u16 _data) { 75#endif
113 return __swap16(_data); 76[[nodiscard]] inline u16 swap16(u16 data) noexcept {
114} 77 return __builtin_bswap16(data);
115inline u32 swap32(u32 _data) {
116 return __swap32(_data);
117}
118inline u64 swap64(u64 _data) {
119 return __swap64(_data);
120}
121#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
122inline u16 swap16(u16 _data) {
123 return bswap16(_data);
124} 78}
125inline u32 swap32(u32 _data) { 79[[nodiscard]] inline u32 swap32(u32 data) noexcept {
126 return bswap32(_data); 80 return __builtin_bswap32(data);
127} 81}
128inline u64 swap64(u64 _data) { 82[[nodiscard]] inline u64 swap64(u64 data) noexcept {
129 return bswap64(_data); 83 return __builtin_bswap64(data);
130} 84}
131#else 85#else
132// Slow generic implementation. 86// Generic implementation.
133inline u16 swap16(u16 data) { 87[[nodiscard]] inline u16 swap16(u16 data) noexcept {
134 return (data >> 8) | (data << 8); 88 return (data >> 8) | (data << 8);
135} 89}
136inline u32 swap32(u32 data) { 90[[nodiscard]] inline u32 swap32(u32 data) noexcept {
137 return (swap16(data) << 16) | swap16(data >> 16); 91 return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) |
92 ((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24);
138} 93}
139inline u64 swap64(u64 data) { 94[[nodiscard]] inline u64 swap64(u64 data) noexcept {
140 return ((u64)swap32(data) << 32) | swap32(data >> 32); 95 return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) |
96 ((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) |
97 ((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) |
98 ((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56);
141} 99}
142#endif 100#endif
143 101
144inline float swapf(float f) { 102[[nodiscard]] inline float swapf(float f) noexcept {
145 static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t."); 103 static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t.");
146 104
147 u32 value; 105 u32 value;
@@ -153,7 +111,7 @@ inline float swapf(float f) {
153 return f; 111 return f;
154} 112}
155 113
156inline double swapd(double f) { 114[[nodiscard]] inline double swapd(double f) noexcept {
157 static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t."); 115 static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t.");
158 116
159 u64 value; 117 u64 value;
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp
new file mode 100644
index 000000000..60a35c67c
--- /dev/null
+++ b/src/common/zstd_compression.cpp
@@ -0,0 +1,53 @@
1// Copyright 2019 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 <algorithm>
8#include <zstd.h>
9
10#include "common/assert.h"
11#include "common/zstd_compression.h"
12
13namespace Common::Compression {
14
15std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level) {
16 compression_level = std::clamp(compression_level, 1, ZSTD_maxCLevel());
17
18 const std::size_t max_compressed_size = ZSTD_compressBound(source_size);
19 std::vector<u8> compressed(max_compressed_size);
20
21 const std::size_t compressed_size =
22 ZSTD_compress(compressed.data(), compressed.size(), source, source_size, compression_level);
23
24 if (ZSTD_isError(compressed_size)) {
25 // Compression failed
26 return {};
27 }
28
29 compressed.resize(compressed_size);
30
31 return compressed;
32}
33
34std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size) {
35 return CompressDataZSTD(source, source_size, ZSTD_CLEVEL_DEFAULT);
36}
37
38std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed) {
39 const std::size_t decompressed_size =
40 ZSTD_getDecompressedSize(compressed.data(), compressed.size());
41 std::vector<u8> decompressed(decompressed_size);
42
43 const std::size_t uncompressed_result_size = ZSTD_decompress(
44 decompressed.data(), decompressed.size(), compressed.data(), compressed.size());
45
46 if (decompressed_size != uncompressed_result_size || ZSTD_isError(uncompressed_result_size)) {
47 // Decompression failed
48 return {};
49 }
50 return decompressed;
51}
52
53} // namespace Common::Compression
diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h
new file mode 100644
index 000000000..e9de941c8
--- /dev/null
+++ b/src/common/zstd_compression.h
@@ -0,0 +1,44 @@
1// Copyright 2019 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 <vector>
8
9#include "common/common_types.h"
10
11namespace Common::Compression {
12
13/**
14 * Compresses a source memory region with Zstandard and returns the compressed data in a vector.
15 *
16 * @param source the uncompressed source memory region.
17 * @param source_size the size in bytes of the uncompressed source memory region.
18 * @param compression_level the used compression level. Should be between 1 and 22.
19 *
20 * @return the compressed data.
21 */
22std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level);
23
24/**
25 * Compresses a source memory region with Zstandard with the default compression level and returns
26 * the compressed data in a vector.
27 *
28 * @param source the uncompressed source memory region.
29 * @param source_size the size in bytes of the uncompressed source memory region.
30 *
31 * @return the compressed data.
32 */
33std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
34
35/**
36 * Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector.
37 *
38 * @param compressed the compressed source memory region.
39 *
40 * @return the decompressed data.
41 */
42std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
43
44} // namespace Common::Compression \ No newline at end of file
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 9e23afe85..c59107102 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -458,7 +458,7 @@ add_library(core STATIC
458create_target_directory_groups(core) 458create_target_directory_groups(core)
459 459
460target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 460target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
461target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives) 461target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives)
462if (ENABLE_WEB_SERVICE) 462if (ENABLE_WEB_SERVICE)
463 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) 463 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
464 target_link_libraries(core PRIVATE web_service) 464 target_link_libraries(core PRIVATE web_service)
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 4fdc12f11..dc96e35d5 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -14,6 +14,7 @@
14#include "core/core_timing.h" 14#include "core/core_timing.h"
15#include "core/core_timing_util.h" 15#include "core/core_timing_util.h"
16#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/kernel.h"
17#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
18#include "core/hle/kernel/svc.h" 19#include "core/hle/kernel/svc.h"
19#include "core/hle/kernel/vm_manager.h" 20#include "core/hle/kernel/vm_manager.h"
@@ -26,7 +27,6 @@ using Vector = Dynarmic::A64::Vector;
26class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks { 27class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
27public: 28public:
28 explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} 29 explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
29 ~ARM_Dynarmic_Callbacks() = default;
30 30
31 u8 MemoryRead8(u64 vaddr) override { 31 u8 MemoryRead8(u64 vaddr) override {
32 return Memory::Read8(vaddr); 32 return Memory::Read8(vaddr);
@@ -100,7 +100,7 @@ public:
100 } 100 }
101 101
102 void CallSVC(u32 swi) override { 102 void CallSVC(u32 swi) override {
103 Kernel::CallSVC(swi); 103 Kernel::CallSVC(parent.system, swi);
104 } 104 }
105 105
106 void AddTicks(u64 ticks) override { 106 void AddTicks(u64 ticks) override {
@@ -113,14 +113,14 @@ public:
113 // Always execute at least one tick. 113 // Always execute at least one tick.
114 amortized_ticks = std::max<u64>(amortized_ticks, 1); 114 amortized_ticks = std::max<u64>(amortized_ticks, 1);
115 115
116 parent.core_timing.AddTicks(amortized_ticks); 116 parent.system.CoreTiming().AddTicks(amortized_ticks);
117 num_interpreted_instructions = 0; 117 num_interpreted_instructions = 0;
118 } 118 }
119 u64 GetTicksRemaining() override { 119 u64 GetTicksRemaining() override {
120 return std::max(parent.core_timing.GetDowncount(), 0); 120 return std::max(parent.system.CoreTiming().GetDowncount(), 0);
121 } 121 }
122 u64 GetCNTPCT() override { 122 u64 GetCNTPCT() override {
123 return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks()); 123 return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks());
124 } 124 }
125 125
126 ARM_Dynarmic& parent; 126 ARM_Dynarmic& parent;
@@ -130,7 +130,7 @@ public:
130}; 130};
131 131
132std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { 132std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
133 auto* current_process = Core::CurrentProcess(); 133 auto* current_process = system.Kernel().CurrentProcess();
134 auto** const page_table = current_process->VMManager().page_table.pointers.data(); 134 auto** const page_table = current_process->VMManager().page_table.pointers.data();
135 135
136 Dynarmic::A64::UserConfig config; 136 Dynarmic::A64::UserConfig config;
@@ -164,7 +164,6 @@ MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)
164 164
165void ARM_Dynarmic::Run() { 165void ARM_Dynarmic::Run() {
166 MICROPROFILE_SCOPE(ARM_Jit_Dynarmic); 166 MICROPROFILE_SCOPE(ARM_Jit_Dynarmic);
167 ASSERT(Memory::GetCurrentPageTable() == current_page_table);
168 167
169 jit->Run(); 168 jit->Run();
170} 169}
@@ -173,10 +172,10 @@ void ARM_Dynarmic::Step() {
173 cb->InterpreterFallback(jit->GetPC(), 1); 172 cb->InterpreterFallback(jit->GetPC(), 1);
174} 173}
175 174
176ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, 175ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
177 std::size_t core_index) 176 std::size_t core_index)
178 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing}, 177 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
179 core_index{core_index}, core_timing{core_timing}, 178 core_index{core_index}, system{system},
180 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} { 179 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
181 ThreadContext ctx{}; 180 ThreadContext ctx{};
182 inner_unicorn.SaveContext(ctx); 181 inner_unicorn.SaveContext(ctx);
@@ -279,7 +278,6 @@ void ARM_Dynarmic::ClearExclusiveState() {
279 278
280void ARM_Dynarmic::PageTableChanged() { 279void ARM_Dynarmic::PageTableChanged() {
281 jit = MakeJit(); 280 jit = MakeJit();
282 current_page_table = Memory::GetCurrentPageTable();
283} 281}
284 282
285DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {} 283DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index aada1e862..c1db254e8 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -12,24 +12,16 @@
12#include "core/arm/exclusive_monitor.h" 12#include "core/arm/exclusive_monitor.h"
13#include "core/arm/unicorn/arm_unicorn.h" 13#include "core/arm/unicorn/arm_unicorn.h"
14 14
15namespace Common {
16struct PageTable;
17}
18
19namespace Core::Timing {
20class CoreTiming;
21}
22
23namespace Core { 15namespace Core {
24 16
25class ARM_Dynarmic_Callbacks; 17class ARM_Dynarmic_Callbacks;
26class DynarmicExclusiveMonitor; 18class DynarmicExclusiveMonitor;
19class System;
27 20
28class ARM_Dynarmic final : public ARM_Interface { 21class ARM_Dynarmic final : public ARM_Interface {
29public: 22public:
30 ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, 23 ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
31 std::size_t core_index); 24 ~ARM_Dynarmic() override;
32 ~ARM_Dynarmic();
33 25
34 void MapBackingMemory(VAddr address, std::size_t size, u8* memory, 26 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
35 Kernel::VMAPermission perms) override; 27 Kernel::VMAPermission perms) override;
@@ -67,16 +59,14 @@ private:
67 ARM_Unicorn inner_unicorn; 59 ARM_Unicorn inner_unicorn;
68 60
69 std::size_t core_index; 61 std::size_t core_index;
70 Timing::CoreTiming& core_timing; 62 System& system;
71 DynarmicExclusiveMonitor& exclusive_monitor; 63 DynarmicExclusiveMonitor& exclusive_monitor;
72
73 Common::PageTable* current_page_table = nullptr;
74}; 64};
75 65
76class DynarmicExclusiveMonitor final : public ExclusiveMonitor { 66class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
77public: 67public:
78 explicit DynarmicExclusiveMonitor(std::size_t core_count); 68 explicit DynarmicExclusiveMonitor(std::size_t core_count);
79 ~DynarmicExclusiveMonitor(); 69 ~DynarmicExclusiveMonitor() override;
80 70
81 void SetExclusive(std::size_t core_index, VAddr addr) override; 71 void SetExclusive(std::size_t core_index, VAddr addr) override;
82 void ClearExclusive() override; 72 void ClearExclusive() override;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index a542a098b..4e07fe8b5 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -10,7 +10,6 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/core_timing.h" 11#include "core/core_timing.h"
12#include "core/hle/kernel/svc.h" 12#include "core/hle/kernel/svc.h"
13#include "core/memory.h"
14 13
15namespace Core { 14namespace Core {
16 15
@@ -49,20 +48,6 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_
49 } 48 }
50} 49}
51 50
52static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
53 u32 esr{};
54 CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
55
56 auto ec = esr >> 26;
57 auto iss = esr & 0xFFFFFF;
58
59 switch (ec) {
60 case 0x15: // SVC
61 Kernel::CallSVC(iss);
62 break;
63 }
64}
65
66static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value, 51static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
67 void* user_data) { 52 void* user_data) {
68 ARM_Interface::ThreadContext ctx{}; 53 ARM_Interface::ThreadContext ctx{};
@@ -72,7 +57,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
72 return {}; 57 return {};
73} 58}
74 59
75ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} { 60ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
76 CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); 61 CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
77 62
78 auto fpv = 3 << 20; 63 auto fpv = 3 << 20;
@@ -177,7 +162,7 @@ void ARM_Unicorn::Run() {
177 if (GDBStub::IsServerEnabled()) { 162 if (GDBStub::IsServerEnabled()) {
178 ExecuteInstructions(std::max(4000000, 0)); 163 ExecuteInstructions(std::max(4000000, 0));
179 } else { 164 } else {
180 ExecuteInstructions(std::max(core_timing.GetDowncount(), 0)); 165 ExecuteInstructions(std::max(system.CoreTiming().GetDowncount(), 0));
181 } 166 }
182} 167}
183 168
@@ -190,14 +175,15 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
190void ARM_Unicorn::ExecuteInstructions(int num_instructions) { 175void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
191 MICROPROFILE_SCOPE(ARM_Jit_Unicorn); 176 MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
192 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); 177 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
193 core_timing.AddTicks(num_instructions); 178 system.CoreTiming().AddTicks(num_instructions);
194 if (GDBStub::IsServerEnabled()) { 179 if (GDBStub::IsServerEnabled()) {
195 if (last_bkpt_hit) { 180 if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
196 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); 181 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
197 } 182 }
183
198 Kernel::Thread* thread = Kernel::GetCurrentThread(); 184 Kernel::Thread* thread = Kernel::GetCurrentThread();
199 SaveContext(thread->GetContext()); 185 SaveContext(thread->GetContext());
200 if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { 186 if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
201 last_bkpt_hit = false; 187 last_bkpt_hit = false;
202 GDBStub::Break(); 188 GDBStub::Break();
203 GDBStub::SendTrap(thread, 5); 189 GDBStub::SendTrap(thread, 5);
@@ -272,4 +258,20 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
272 last_bkpt_hit = true; 258 last_bkpt_hit = true;
273} 259}
274 260
261void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {
262 u32 esr{};
263 CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
264
265 const auto ec = esr >> 26;
266 const auto iss = esr & 0xFFFFFF;
267
268 auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data);
269
270 switch (ec) {
271 case 0x15: // SVC
272 Kernel::CallSVC(arm_instance->system, iss);
273 break;
274 }
275}
276
275} // namespace Core 277} // namespace Core
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index dbd6955ea..209fc16ad 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -9,16 +9,14 @@
9#include "core/arm/arm_interface.h" 9#include "core/arm/arm_interface.h"
10#include "core/gdbstub/gdbstub.h" 10#include "core/gdbstub/gdbstub.h"
11 11
12namespace Core::Timing {
13class CoreTiming;
14}
15
16namespace Core { 12namespace Core {
17 13
14class System;
15
18class ARM_Unicorn final : public ARM_Interface { 16class ARM_Unicorn final : public ARM_Interface {
19public: 17public:
20 explicit ARM_Unicorn(Timing::CoreTiming& core_timing); 18 explicit ARM_Unicorn(System& system);
21 ~ARM_Unicorn(); 19 ~ARM_Unicorn() override;
22 20
23 void MapBackingMemory(VAddr address, std::size_t size, u8* memory, 21 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
24 Kernel::VMAPermission perms) override; 22 Kernel::VMAPermission perms) override;
@@ -47,10 +45,12 @@ public:
47 void RecordBreak(GDBStub::BreakpointAddress bkpt); 45 void RecordBreak(GDBStub::BreakpointAddress bkpt);
48 46
49private: 47private:
48 static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
49
50 uc_engine* uc{}; 50 uc_engine* uc{};
51 Timing::CoreTiming& core_timing; 51 System& system;
52 GDBStub::BreakpointAddress last_bkpt{}; 52 GDBStub::BreakpointAddress last_bkpt{};
53 bool last_bkpt_hit; 53 bool last_bkpt_hit = false;
54}; 54};
55 55
56} // namespace Core 56} // namespace Core
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 4fe77c25b..bc9e887b6 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -17,6 +17,7 @@
17#include "core/core_timing.h" 17#include "core/core_timing.h"
18#include "core/cpu_core_manager.h" 18#include "core/cpu_core_manager.h"
19#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
20#include "core/file_sys/registered_cache.h"
20#include "core/file_sys/vfs_concat.h" 21#include "core/file_sys/vfs_concat.h"
21#include "core/file_sys/vfs_real.h" 22#include "core/file_sys/vfs_real.h"
22#include "core/gdbstub/gdbstub.h" 23#include "core/gdbstub/gdbstub.h"
@@ -108,6 +109,8 @@ struct System::Impl {
108 // Create a default fs if one doesn't already exist. 109 // Create a default fs if one doesn't already exist.
109 if (virtual_filesystem == nullptr) 110 if (virtual_filesystem == nullptr)
110 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 111 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
112 if (content_provider == nullptr)
113 content_provider = std::make_unique<FileSys::ContentProviderUnion>();
111 114
112 /// Create default implementations of applets if one is not provided. 115 /// Create default implementations of applets if one is not provided.
113 if (profile_selector == nullptr) 116 if (profile_selector == nullptr)
@@ -249,6 +252,8 @@ struct System::Impl {
249 Kernel::KernelCore kernel; 252 Kernel::KernelCore kernel;
250 /// RealVfsFilesystem instance 253 /// RealVfsFilesystem instance
251 FileSys::VirtualFilesystem virtual_filesystem; 254 FileSys::VirtualFilesystem virtual_filesystem;
255 /// ContentProviderUnion instance
256 std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
252 /// AppLoader used to load the current executing application 257 /// AppLoader used to load the current executing application
253 std::unique_ptr<Loader::AppLoader> app_loader; 258 std::unique_ptr<Loader::AppLoader> app_loader;
254 std::unique_ptr<VideoCore::RendererBase> renderer; 259 std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -488,6 +493,27 @@ const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
488 return *impl->software_keyboard; 493 return *impl->software_keyboard;
489} 494}
490 495
496void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
497 impl->content_provider = std::move(provider);
498}
499
500FileSys::ContentProvider& System::GetContentProvider() {
501 return *impl->content_provider;
502}
503
504const FileSys::ContentProvider& System::GetContentProvider() const {
505 return *impl->content_provider;
506}
507
508void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
509 FileSys::ContentProvider* provider) {
510 impl->content_provider->SetSlot(slot, provider);
511}
512
513void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
514 impl->content_provider->ClearSlot(slot);
515}
516
491void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) { 517void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
492 impl->web_browser = std::move(applet); 518 impl->web_browser = std::move(applet);
493} 519}
diff --git a/src/core/core.h b/src/core/core.h
index 4d83b93cc..82b2e087e 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -21,6 +21,9 @@ class WebBrowserApplet;
21 21
22namespace FileSys { 22namespace FileSys {
23class CheatList; 23class CheatList;
24class ContentProvider;
25class ContentProviderUnion;
26enum class ContentProviderUnionSlot;
24class VfsFilesystem; 27class VfsFilesystem;
25} // namespace FileSys 28} // namespace FileSys
26 29
@@ -270,6 +273,17 @@ public:
270 Frontend::WebBrowserApplet& GetWebBrowser(); 273 Frontend::WebBrowserApplet& GetWebBrowser();
271 const Frontend::WebBrowserApplet& GetWebBrowser() const; 274 const Frontend::WebBrowserApplet& GetWebBrowser() const;
272 275
276 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
277
278 FileSys::ContentProvider& GetContentProvider();
279
280 const FileSys::ContentProvider& GetContentProvider() const;
281
282 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
283 FileSys::ContentProvider* provider);
284
285 void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
286
273private: 287private:
274 System(); 288 System();
275 289
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index e75741db0..ba63c3e61 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -55,13 +55,13 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
55 : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { 55 : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
56 if (Settings::values.use_cpu_jit) { 56 if (Settings::values.use_cpu_jit) {
57#ifdef ARCHITECTURE_x86_64 57#ifdef ARCHITECTURE_x86_64
58 arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index); 58 arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
59#else 59#else
60 arm_interface = std::make_unique<ARM_Unicorn>(); 60 arm_interface = std::make_unique<ARM_Unicorn>(system);
61 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 61 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
62#endif 62#endif
63 } else { 63 } else {
64 arm_interface = std::make_unique<ARM_Unicorn>(core_timing); 64 arm_interface = std::make_unique<ARM_Unicorn>(system);
65 } 65 }
66 66
67 scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface); 67 scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index dfac9a4b3..dc006e2bb 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -22,6 +22,7 @@
22#include "common/file_util.h" 22#include "common/file_util.h"
23#include "common/hex_util.h" 23#include "common/hex_util.h"
24#include "common/logging/log.h" 24#include "common/logging/log.h"
25#include "core/core.h"
25#include "core/crypto/aes_util.h" 26#include "core/crypto/aes_util.h"
26#include "core/crypto/key_manager.h" 27#include "core/crypto/key_manager.h"
27#include "core/crypto/partition_data_manager.h" 28#include "core/crypto/partition_data_manager.h"
@@ -794,7 +795,7 @@ void KeyManager::DeriveBase() {
794 795
795void KeyManager::DeriveETicket(PartitionDataManager& data) { 796void KeyManager::DeriveETicket(PartitionDataManager& data) {
796 // ETicket keys 797 // ETicket keys
797 const auto es = Service::FileSystem::GetUnionContents().GetEntry( 798 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
798 0x0100000000000033, FileSys::ContentRecordType::Program); 799 0x0100000000000033, FileSys::ContentRecordType::Program);
799 800
800 if (es == nullptr) 801 if (es == nullptr)
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 83c184750..60ea9ad12 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -67,7 +67,7 @@ std::string NACP::GetDeveloperName(Language language) const {
67} 67}
68 68
69u64 NACP::GetTitleId() const { 69u64 NACP::GetTitleId() const {
70 return raw.title_id; 70 return raw.save_data_owner_id;
71} 71}
72 72
73u64 NACP::GetDLCBaseTitleId() const { 73u64 NACP::GetDLCBaseTitleId() const {
@@ -80,11 +80,11 @@ std::string NACP::GetVersionString() const {
80} 80}
81 81
82u64 NACP::GetDefaultNormalSaveSize() const { 82u64 NACP::GetDefaultNormalSaveSize() const {
83 return raw.normal_save_data_size; 83 return raw.user_account_save_data_size;
84} 84}
85 85
86u64 NACP::GetDefaultJournalSaveSize() const { 86u64 NACP::GetDefaultJournalSaveSize() const {
87 return raw.journal_sava_data_size; 87 return raw.user_account_save_data_journal_size;
88} 88}
89 89
90std::vector<u8> NACP::GetRawBytes() const { 90std::vector<u8> NACP::GetRawBytes() const {
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 7b9cdc910..280710ddf 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -38,23 +38,35 @@ struct RawNACP {
38 u8 video_capture_mode; 38 u8 video_capture_mode;
39 bool data_loss_confirmation; 39 bool data_loss_confirmation;
40 INSERT_PADDING_BYTES(1); 40 INSERT_PADDING_BYTES(1);
41 u64_le title_id; 41 u64_le presence_group_id;
42 std::array<u8, 0x20> rating_age; 42 std::array<u8, 0x20> rating_age;
43 std::array<char, 0x10> version_string; 43 std::array<char, 0x10> version_string;
44 u64_le dlc_base_title_id; 44 u64_le dlc_base_title_id;
45 u64_le title_id_2; 45 u64_le save_data_owner_id;
46 u64_le normal_save_data_size; 46 u64_le user_account_save_data_size;
47 u64_le journal_sava_data_size; 47 u64_le user_account_save_data_journal_size;
48 INSERT_PADDING_BYTES(0x18); 48 u64_le device_save_data_size;
49 u64_le product_code; 49 u64_le device_save_data_journal_size;
50 u64_le bcat_delivery_cache_storage_size;
51 char application_error_code_category[8];
50 std::array<u64_le, 0x8> local_communication; 52 std::array<u64_le, 0x8> local_communication;
51 u8 logo_type; 53 u8 logo_type;
52 u8 logo_handling; 54 u8 logo_handling;
53 bool runtime_add_on_content_install; 55 bool runtime_add_on_content_install;
54 INSERT_PADDING_BYTES(5); 56 INSERT_PADDING_BYTES(5);
55 u64_le title_id_update; 57 u64_le seed_for_pseudo_device_id;
56 std::array<u8, 0x40> bcat_passphrase; 58 std::array<u8, 0x41> bcat_passphrase;
57 INSERT_PADDING_BYTES(0xEC0); 59 INSERT_PADDING_BYTES(7);
60 u64_le user_account_save_data_max_size;
61 u64_le user_account_save_data_max_journal_size;
62 u64_le device_save_data_max_size;
63 u64_le device_save_data_max_journal_size;
64 u64_le temporary_storage_size;
65 u64_le cache_storage_size;
66 u64_le cache_storage_journal_size;
67 u64_le cache_storage_data_and_journal_max_size;
68 u64_le cache_storage_max_index;
69 INSERT_PADDING_BYTES(0xE70);
58}; 70};
59static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size."); 71static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
60 72
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index 47b7526c7..d126ae8dd 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -23,6 +23,7 @@
23 */ 23 */
24 24
25#include <cstring> 25#include <cstring>
26#include <string_view>
26#include "common/alignment.h" 27#include "common/alignment.h"
27#include "common/assert.h" 28#include "common/assert.h"
28#include "core/file_sys/fsmitm_romfsbuild.h" 29#include "core/file_sys/fsmitm_romfsbuild.h"
@@ -97,7 +98,8 @@ struct RomFSBuildFileContext {
97 VirtualFile source; 98 VirtualFile source;
98}; 99};
99 100
100static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) { 101static u32 romfs_calc_path_hash(u32 parent, std::string_view path, u32 start,
102 std::size_t path_len) {
101 u32 hash = parent ^ 123456789; 103 u32 hash = parent ^ 123456789;
102 for (u32 i = 0; i < path_len; i++) { 104 for (u32 i = 0; i < path_len; i++) {
103 hash = (hash >> 5) | (hash << 27); 105 hash = (hash >> 5) | (hash << 27);
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index 6f34b7836..93d0df6b9 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -10,14 +10,6 @@
10 10
11namespace FileSys { 11namespace FileSys {
12 12
13bool operator>=(TitleType lhs, TitleType rhs) {
14 return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs);
15}
16
17bool operator<=(TitleType lhs, TitleType rhs) {
18 return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs);
19}
20
21CNMT::CNMT(VirtualFile file) { 13CNMT::CNMT(VirtualFile file) {
22 if (file->ReadObject(&header) != sizeof(CNMTHeader)) 14 if (file->ReadObject(&header) != sizeof(CNMTHeader))
23 return; 15 return;
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index a05d155f4..50bf38471 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -29,9 +29,6 @@ enum class TitleType : u8 {
29 DeltaTitle = 0x83, 29 DeltaTitle = 0x83,
30}; 30};
31 31
32bool operator>=(TitleType lhs, TitleType rhs);
33bool operator<=(TitleType lhs, TitleType rhs);
34
35enum class ContentRecordType : u8 { 32enum class ContentRecordType : u8 {
36 Meta = 0, 33 Meta = 0,
37 Program = 1, 34 Program = 1,
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index e11217708..78dbadee3 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -10,6 +10,7 @@
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/hex_util.h" 11#include "common/hex_util.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "core/core.h"
13#include "core/file_sys/content_archive.h" 14#include "core/file_sys/content_archive.h"
14#include "core/file_sys/control_metadata.h" 15#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/ips_layer.h" 16#include "core/file_sys/ips_layer.h"
@@ -69,7 +70,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
69 } 70 }
70 } 71 }
71 72
72 const auto installed = Service::FileSystem::GetUnionContents(); 73 const auto& installed = Core::System::GetInstance().GetContentProvider();
73 74
74 const auto& disabled = Settings::values.disabled_addons[title_id]; 75 const auto& disabled = Settings::values.disabled_addons[title_id];
75 const auto update_disabled = 76 const auto update_disabled =
@@ -155,7 +156,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
155 return out; 156 return out;
156} 157}
157 158
158std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { 159std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::string& name) const {
159 if (nso.size() < sizeof(Loader::NSOHeader)) { 160 if (nso.size() < sizeof(Loader::NSOHeader)) {
160 return nso; 161 return nso;
161 } 162 }
@@ -171,18 +172,19 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
171 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 172 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
172 173
173 if (Settings::values.dump_nso) { 174 if (Settings::values.dump_nso) {
174 LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id); 175 LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
176 title_id);
175 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); 177 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
176 if (dump_dir != nullptr) { 178 if (dump_dir != nullptr) {
177 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); 179 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
178 const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id)); 180 const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
179 181
180 file->Resize(nso.size()); 182 file->Resize(nso.size());
181 file->WriteBytes(nso); 183 file->WriteBytes(nso);
182 } 184 }
183 } 185 }
184 186
185 LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); 187 LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
186 188
187 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 189 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
188 auto patch_dirs = load_dir->GetSubdirectories(); 190 auto patch_dirs = load_dir->GetSubdirectories();
@@ -345,7 +347,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
345 if (romfs == nullptr) 347 if (romfs == nullptr)
346 return romfs; 348 return romfs;
347 349
348 const auto installed = Service::FileSystem::GetUnionContents(); 350 const auto& installed = Core::System::GetInstance().GetContentProvider();
349 351
350 // Game Updates 352 // Game Updates
351 const auto update_tid = GetUpdateTitleID(title_id); 353 const auto update_tid = GetUpdateTitleID(title_id);
@@ -392,7 +394,7 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
392std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( 394std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
393 VirtualFile update_raw) const { 395 VirtualFile update_raw) const {
394 std::map<std::string, std::string, std::less<>> out; 396 std::map<std::string, std::string, std::less<>> out;
395 const auto installed = Service::FileSystem::GetUnionContents(); 397 const auto& installed = Core::System::GetInstance().GetContentProvider();
396 const auto& disabled = Settings::values.disabled_addons[title_id]; 398 const auto& disabled = Settings::values.disabled_addons[title_id];
397 399
398 // Game Updates 400 // Game Updates
@@ -466,10 +468,10 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
466 468
467 // DLC 469 // DLC
468 const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); 470 const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
469 std::vector<RegisteredCacheEntry> dlc_match; 471 std::vector<ContentProviderEntry> dlc_match;
470 dlc_match.reserve(dlc_entries.size()); 472 dlc_match.reserve(dlc_entries.size());
471 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), 473 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
472 [this, &installed](const RegisteredCacheEntry& entry) { 474 [this, &installed](const ContentProviderEntry& entry) {
473 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id && 475 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
474 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; 476 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
475 }); 477 });
@@ -492,7 +494,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
492} 494}
493 495
494std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 496std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
495 const auto installed{Service::FileSystem::GetUnionContents()}; 497 const auto& installed = Core::System::GetInstance().GetContentProvider();
496 498
497 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); 499 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
498 if (base_control_nca == nullptr) 500 if (base_control_nca == nullptr)
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index de2672c76..769f8c6f0 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -44,7 +44,7 @@ public:
44 // Currently tracked NSO patches: 44 // Currently tracked NSO patches:
45 // - IPS 45 // - IPS
46 // - IPSwitch 46 // - IPSwitch
47 std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; 47 std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const;
48 48
49 // Checks to see if PatchNSO() will have any effect given the NSO's build ID. 49 // Checks to see if PatchNSO() will have any effect given the NSO's build ID.
50 // Used to prevent expensive copies in NSO loader. 50 // Used to prevent expensive copies in NSO loader.
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index d3e00437f..d863253f8 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.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 <cstddef> 5#include <cstddef>
6#include <cstring>
7#include <vector> 6#include <vector>
8 7
9#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -17,28 +16,30 @@ ProgramMetadata::ProgramMetadata() = default;
17ProgramMetadata::~ProgramMetadata() = default; 16ProgramMetadata::~ProgramMetadata() = default;
18 17
19Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { 18Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
20 std::size_t total_size = static_cast<std::size_t>(file->GetSize()); 19 const std::size_t total_size = file->GetSize();
21 if (total_size < sizeof(Header)) 20 if (total_size < sizeof(Header)) {
22 return Loader::ResultStatus::ErrorBadNPDMHeader; 21 return Loader::ResultStatus::ErrorBadNPDMHeader;
22 }
23 23
24 // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable. 24 if (sizeof(Header) != file->ReadObject(&npdm_header)) {
25 std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
26 if (sizeof(Header) != npdm_header_data.size())
27 return Loader::ResultStatus::ErrorBadNPDMHeader; 25 return Loader::ResultStatus::ErrorBadNPDMHeader;
28 std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header)); 26 }
29 27
30 std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset); 28 if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset)) {
31 if (sizeof(AcidHeader) != acid_header_data.size())
32 return Loader::ResultStatus::ErrorBadACIDHeader; 29 return Loader::ResultStatus::ErrorBadACIDHeader;
33 std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader)); 30 }
34 31
35 if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) 32 if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) {
36 return Loader::ResultStatus::ErrorBadACIHeader; 33 return Loader::ResultStatus::ErrorBadACIHeader;
34 }
37 35
38 if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) 36 if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) {
39 return Loader::ResultStatus::ErrorBadFileAccessControl; 37 return Loader::ResultStatus::ErrorBadFileAccessControl;
40 if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) 38 }
39
40 if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) {
41 return Loader::ResultStatus::ErrorBadFileAccessHeader; 41 return Loader::ResultStatus::ErrorBadFileAccessHeader;
42 }
42 43
43 aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32)); 44 aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
44 const u64 read_size = aci_header.kac_size; 45 const u64 read_size = aci_header.kac_size;
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 0033ba347..7de5b9cf9 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -58,7 +58,6 @@ public:
58 void Print() const; 58 void Print() const;
59 59
60private: 60private:
61 // TODO(DarkLordZach): BitField is not trivially copyable.
62 struct Header { 61 struct Header {
63 std::array<char, 4> magic; 62 std::array<char, 4> magic;
64 std::array<u8, 8> reserved; 63 std::array<u8, 8> reserved;
@@ -85,7 +84,6 @@ private:
85 84
86 static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); 85 static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
87 86
88 // TODO(DarkLordZach): BitField is not trivially copyable.
89 struct AcidHeader { 87 struct AcidHeader {
90 std::array<u8, 0x100> signature; 88 std::array<u8, 0x100> signature;
91 std::array<u8, 0x100> nca_modulus; 89 std::array<u8, 0x100> nca_modulus;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 1c6bacace..3946ff871 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -23,19 +23,19 @@ namespace FileSys {
23// The size of blocks to use when vfs raw copying into nand. 23// The size of blocks to use when vfs raw copying into nand.
24constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000; 24constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
25 25
26std::string RegisteredCacheEntry::DebugInfo() const { 26std::string ContentProviderEntry::DebugInfo() const {
27 return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type)); 27 return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
28} 28}
29 29
30bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { 30bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
31 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); 31 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
32} 32}
33 33
34bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { 34bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type); 35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
36} 36}
37 37
38bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { 38bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
39 return !operator==(lhs, rhs); 39 return !operator==(lhs, rhs);
40} 40}
41 41
@@ -84,7 +84,7 @@ static std::string GetCNMTName(TitleType type, u64 title_id) {
84 return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); 84 return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
85} 85}
86 86
87static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { 87ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
88 switch (type) { 88 switch (type) {
89 case NCAContentType::Program: 89 case NCAContentType::Program:
90 // TODO(DarkLordZach): Differentiate between Program and Patch 90 // TODO(DarkLordZach): Differentiate between Program and Patch
@@ -104,6 +104,28 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
104 } 104 }
105} 105}
106 106
107ContentProvider::~ContentProvider() = default;
108
109bool ContentProvider::HasEntry(ContentProviderEntry entry) const {
110 return HasEntry(entry.title_id, entry.type);
111}
112
113VirtualFile ContentProvider::GetEntryUnparsed(ContentProviderEntry entry) const {
114 return GetEntryUnparsed(entry.title_id, entry.type);
115}
116
117VirtualFile ContentProvider::GetEntryRaw(ContentProviderEntry entry) const {
118 return GetEntryRaw(entry.title_id, entry.type);
119}
120
121std::unique_ptr<NCA> ContentProvider::GetEntry(ContentProviderEntry entry) const {
122 return GetEntry(entry.title_id, entry.type);
123}
124
125std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
126 return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
127}
128
107VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, 129VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
108 std::string_view path) const { 130 std::string_view path) const {
109 const auto file = dir->GetFileRelative(path); 131 const auto file = dir->GetFileRelative(path);
@@ -161,8 +183,8 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
161 return file; 183 return file;
162} 184}
163 185
164static std::optional<NcaID> CheckMapForContentRecord( 186static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id,
165 const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) { 187 ContentRecordType type) {
166 if (map.find(title_id) == map.end()) 188 if (map.find(title_id) == map.end())
167 return {}; 189 return {};
168 190
@@ -268,7 +290,7 @@ void RegisteredCache::Refresh() {
268 AccumulateYuzuMeta(); 290 AccumulateYuzuMeta();
269} 291}
270 292
271RegisteredCache::RegisteredCache(VirtualDir dir_, RegisteredCacheParsingFunction parsing_function) 293RegisteredCache::RegisteredCache(VirtualDir dir_, ContentProviderParsingFunction parsing_function)
272 : dir(std::move(dir_)), parser(std::move(parsing_function)) { 294 : dir(std::move(dir_)), parser(std::move(parsing_function)) {
273 Refresh(); 295 Refresh();
274} 296}
@@ -279,19 +301,11 @@ bool RegisteredCache::HasEntry(u64 title_id, ContentRecordType type) const {
279 return GetEntryRaw(title_id, type) != nullptr; 301 return GetEntryRaw(title_id, type) != nullptr;
280} 302}
281 303
282bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
283 return GetEntryRaw(entry) != nullptr;
284}
285
286VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { 304VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
287 const auto id = GetNcaIDFromMetadata(title_id, type); 305 const auto id = GetNcaIDFromMetadata(title_id, type);
288 return id ? GetFileAtID(*id) : nullptr; 306 return id ? GetFileAtID(*id) : nullptr;
289} 307}
290 308
291VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
292 return GetEntryUnparsed(entry.title_id, entry.type);
293}
294
295std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { 309std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
296 const auto meta_iter = meta.find(title_id); 310 const auto meta_iter = meta.find(title_id);
297 if (meta_iter != meta.end()) 311 if (meta_iter != meta.end())
@@ -309,10 +323,6 @@ VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) c
309 return id ? parser(GetFileAtID(*id), *id) : nullptr; 323 return id ? parser(GetFileAtID(*id), *id) : nullptr;
310} 324}
311 325
312VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
313 return GetEntryRaw(entry.title_id, entry.type);
314}
315
316std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { 326std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
317 const auto raw = GetEntryRaw(title_id, type); 327 const auto raw = GetEntryRaw(title_id, type);
318 if (raw == nullptr) 328 if (raw == nullptr)
@@ -320,10 +330,6 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
320 return std::make_unique<NCA>(raw, nullptr, 0, keys); 330 return std::make_unique<NCA>(raw, nullptr, 0, keys);
321} 331}
322 332
323std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
324 return GetEntry(entry.title_id, entry.type);
325}
326
327template <typename T> 333template <typename T>
328void RegisteredCache::IterateAllMetadata( 334void RegisteredCache::IterateAllMetadata(
329 std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc, 335 std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc,
@@ -348,25 +354,14 @@ void RegisteredCache::IterateAllMetadata(
348 } 354 }
349} 355}
350 356
351std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const { 357std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
352 std::vector<RegisteredCacheEntry> out;
353 IterateAllMetadata<RegisteredCacheEntry>(
354 out,
355 [](const CNMT& c, const ContentRecord& r) {
356 return RegisteredCacheEntry{c.GetTitleID(), r.type};
357 },
358 [](const CNMT& c, const ContentRecord& r) { return true; });
359 return out;
360}
361
362std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
363 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, 358 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
364 std::optional<u64> title_id) const { 359 std::optional<u64> title_id) const {
365 std::vector<RegisteredCacheEntry> out; 360 std::vector<ContentProviderEntry> out;
366 IterateAllMetadata<RegisteredCacheEntry>( 361 IterateAllMetadata<ContentProviderEntry>(
367 out, 362 out,
368 [](const CNMT& c, const ContentRecord& r) { 363 [](const CNMT& c, const ContentRecord& r) {
369 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 364 return ContentProviderEntry{c.GetTitleID(), r.type};
370 }, 365 },
371 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { 366 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
372 if (title_type && *title_type != c.GetType()) 367 if (title_type && *title_type != c.GetType())
@@ -521,37 +516,56 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
521 }) != yuzu_meta.end(); 516 }) != yuzu_meta.end();
522} 517}
523 518
524RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches) 519ContentProviderUnion::~ContentProviderUnion() = default;
525 : caches(std::move(caches)) {}
526 520
527void RegisteredCacheUnion::Refresh() { 521void ContentProviderUnion::SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider) {
528 for (const auto& c : caches) 522 providers[slot] = provider;
529 c->Refresh();
530} 523}
531 524
532bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const { 525void ContentProviderUnion::ClearSlot(ContentProviderUnionSlot slot) {
533 return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) { 526 providers[slot] = nullptr;
534 return cache->HasEntry(title_id, type);
535 });
536} 527}
537 528
538bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const { 529void ContentProviderUnion::Refresh() {
539 return HasEntry(entry.title_id, entry.type); 530 for (auto& provider : providers) {
531 if (provider.second == nullptr)
532 continue;
533
534 provider.second->Refresh();
535 }
540} 536}
541 537
542std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const { 538bool ContentProviderUnion::HasEntry(u64 title_id, ContentRecordType type) const {
543 for (const auto& c : caches) { 539 for (const auto& provider : providers) {
544 const auto res = c->GetEntryVersion(title_id); 540 if (provider.second == nullptr)
545 if (res) 541 continue;
542
543 if (provider.second->HasEntry(title_id, type))
544 return true;
545 }
546
547 return false;
548}
549
550std::optional<u32> ContentProviderUnion::GetEntryVersion(u64 title_id) const {
551 for (const auto& provider : providers) {
552 if (provider.second == nullptr)
553 continue;
554
555 const auto res = provider.second->GetEntryVersion(title_id);
556 if (res != std::nullopt)
546 return res; 557 return res;
547 } 558 }
548 559
549 return {}; 560 return std::nullopt;
550} 561}
551 562
552VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { 563VirtualFile ContentProviderUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
553 for (const auto& c : caches) { 564 for (const auto& provider : providers) {
554 const auto res = c->GetEntryUnparsed(title_id, type); 565 if (provider.second == nullptr)
566 continue;
567
568 const auto res = provider.second->GetEntryUnparsed(title_id, type);
555 if (res != nullptr) 569 if (res != nullptr)
556 return res; 570 return res;
557 } 571 }
@@ -559,13 +573,12 @@ VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordTy
559 return nullptr; 573 return nullptr;
560} 574}
561 575
562VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const { 576VirtualFile ContentProviderUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
563 return GetEntryUnparsed(entry.title_id, entry.type); 577 for (const auto& provider : providers) {
564} 578 if (provider.second == nullptr)
579 continue;
565 580
566VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const { 581 const auto res = provider.second->GetEntryRaw(title_id, type);
567 for (const auto& c : caches) {
568 const auto res = c->GetEntryRaw(title_id, type);
569 if (res != nullptr) 582 if (res != nullptr)
570 return res; 583 return res;
571 } 584 }
@@ -573,30 +586,56 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType ty
573 return nullptr; 586 return nullptr;
574} 587}
575 588
576VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const { 589std::unique_ptr<NCA> ContentProviderUnion::GetEntry(u64 title_id, ContentRecordType type) const {
577 return GetEntryRaw(entry.title_id, entry.type); 590 for (const auto& provider : providers) {
578} 591 if (provider.second == nullptr)
592 continue;
579 593
580std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { 594 auto res = provider.second->GetEntry(title_id, type);
581 const auto raw = GetEntryRaw(title_id, type); 595 if (res != nullptr)
582 if (raw == nullptr) 596 return res;
583 return nullptr; 597 }
584 return std::make_unique<NCA>(raw); 598
599 return nullptr;
585} 600}
586 601
587std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { 602std::vector<ContentProviderEntry> ContentProviderUnion::ListEntriesFilter(
588 return GetEntry(entry.title_id, entry.type); 603 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
604 std::optional<u64> title_id) const {
605 std::vector<ContentProviderEntry> out;
606
607 for (const auto& provider : providers) {
608 if (provider.second == nullptr)
609 continue;
610
611 const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
612 std::copy(vec.begin(), vec.end(), std::back_inserter(out));
613 }
614
615 std::sort(out.begin(), out.end());
616 out.erase(std::unique(out.begin(), out.end()), out.end());
617 return out;
589} 618}
590 619
591std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const { 620std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>>
592 std::vector<RegisteredCacheEntry> out; 621ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnionSlot> origin,
593 for (const auto& c : caches) { 622 std::optional<TitleType> title_type,
594 c->IterateAllMetadata<RegisteredCacheEntry>( 623 std::optional<ContentRecordType> record_type,
595 out, 624 std::optional<u64> title_id) const {
596 [](const CNMT& c, const ContentRecord& r) { 625 std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> out;
597 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 626
598 }, 627 for (const auto& provider : providers) {
599 [](const CNMT& c, const ContentRecord& r) { return true; }); 628 if (provider.second == nullptr)
629 continue;
630
631 if (origin.has_value() && *origin != provider.first)
632 continue;
633
634 const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
635 std::transform(vec.begin(), vec.end(), std::back_inserter(out),
636 [&provider](const ContentProviderEntry& entry) {
637 return std::make_pair(provider.first, entry);
638 });
600 } 639 }
601 640
602 std::sort(out.begin(), out.end()); 641 std::sort(out.begin(), out.end());
@@ -604,25 +643,61 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
604 return out; 643 return out;
605} 644}
606 645
607std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter( 646ManualContentProvider::~ManualContentProvider() = default;
647
648void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,
649 u64 title_id, VirtualFile file) {
650 entries.insert_or_assign({title_type, content_type, title_id}, file);
651}
652
653void ManualContentProvider::ClearAllEntries() {
654 entries.clear();
655}
656
657void ManualContentProvider::Refresh() {}
658
659bool ManualContentProvider::HasEntry(u64 title_id, ContentRecordType type) const {
660 return GetEntryRaw(title_id, type) != nullptr;
661}
662
663std::optional<u32> ManualContentProvider::GetEntryVersion(u64 title_id) const {
664 return std::nullopt;
665}
666
667VirtualFile ManualContentProvider::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
668 return GetEntryRaw(title_id, type);
669}
670
671VirtualFile ManualContentProvider::GetEntryRaw(u64 title_id, ContentRecordType type) const {
672 const auto iter =
673 std::find_if(entries.begin(), entries.end(), [title_id, type](const auto& entry) {
674 const auto [title_type, content_type, e_title_id] = entry.first;
675 return content_type == type && e_title_id == title_id;
676 });
677 if (iter == entries.end())
678 return nullptr;
679 return iter->second;
680}
681
682std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecordType type) const {
683 const auto res = GetEntryRaw(title_id, type);
684 if (res == nullptr)
685 return nullptr;
686 return std::make_unique<NCA>(res, nullptr, 0, keys);
687}
688
689std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter(
608 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, 690 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
609 std::optional<u64> title_id) const { 691 std::optional<u64> title_id) const {
610 std::vector<RegisteredCacheEntry> out; 692 std::vector<ContentProviderEntry> out;
611 for (const auto& c : caches) { 693
612 c->IterateAllMetadata<RegisteredCacheEntry>( 694 for (const auto& entry : entries) {
613 out, 695 const auto [e_title_type, e_content_type, e_title_id] = entry.first;
614 [](const CNMT& c, const ContentRecord& r) { 696 if ((title_type == std::nullopt || e_title_type == *title_type) &&
615 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 697 (record_type == std::nullopt || e_content_type == *record_type) &&
616 }, 698 (title_id == std::nullopt || e_title_id == *title_id)) {
617 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { 699 out.emplace_back(ContentProviderEntry{e_title_id, e_content_type});
618 if (title_type && *title_type != c.GetType()) 700 }
619 return false;
620 if (record_type && *record_type != r.type)
621 return false;
622 if (title_id && *title_id != c.GetTitleID())
623 return false;
624 return true;
625 });
626 } 701 }
627 702
628 std::sort(out.begin(), out.end()); 703 std::sort(out.begin(), out.end());
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 3b77af4e0..ec9052653 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -21,12 +21,13 @@ class NSP;
21class XCI; 21class XCI;
22 22
23enum class ContentRecordType : u8; 23enum class ContentRecordType : u8;
24enum class NCAContentType : u8;
24enum class TitleType : u8; 25enum class TitleType : u8;
25 26
26struct ContentRecord; 27struct ContentRecord;
27 28
28using NcaID = std::array<u8, 0x10>; 29using NcaID = std::array<u8, 0x10>;
29using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; 30using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
30using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>; 31using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
31 32
32enum class InstallResult { 33enum class InstallResult {
@@ -36,7 +37,7 @@ enum class InstallResult {
36 ErrorMetaFailed, 37 ErrorMetaFailed,
37}; 38};
38 39
39struct RegisteredCacheEntry { 40struct ContentProviderEntry {
40 u64 title_id; 41 u64 title_id;
41 ContentRecordType type; 42 ContentRecordType type;
42 43
@@ -47,12 +48,46 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
47 return base_title_id | 0x800; 48 return base_title_id | 0x800;
48} 49}
49 50
51ContentRecordType GetCRTypeFromNCAType(NCAContentType type);
52
50// boost flat_map requires operator< for O(log(n)) lookups. 53// boost flat_map requires operator< for O(log(n)) lookups.
51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 54bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
52 55
53// std unique requires operator== to identify duplicates. 56// std unique requires operator== to identify duplicates.
54bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 57bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
55bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 58bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
59
60class ContentProvider {
61public:
62 virtual ~ContentProvider();
63
64 virtual void Refresh() = 0;
65
66 virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0;
67 virtual bool HasEntry(ContentProviderEntry entry) const;
68
69 virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;
70
71 virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0;
72 virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
73
74 virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0;
75 virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
76
77 virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0;
78 virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
79
80 virtual std::vector<ContentProviderEntry> ListEntries() const;
81
82 // If a parameter is not std::nullopt, it will be filtered for from all entries.
83 virtual std::vector<ContentProviderEntry> ListEntriesFilter(
84 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
85 std::optional<u64> title_id = {}) const = 0;
86
87protected:
88 // A single instance of KeyManager to be used by GetEntry()
89 Core::Crypto::KeyManager keys;
90};
56 91
57/* 92/*
58 * A class that catalogues NCAs in the registered directory structure. 93 * A class that catalogues NCAs in the registered directory structure.
@@ -67,39 +102,32 @@ bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs
67 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient 102 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient
68 * when 4GB splitting can be ignored.) 103 * when 4GB splitting can be ignored.)
69 */ 104 */
70class RegisteredCache { 105class RegisteredCache : public ContentProvider {
71 friend class RegisteredCacheUnion;
72
73public: 106public:
74 // Parsing function defines the conversion from raw file to NCA. If there are other steps 107 // Parsing function defines the conversion from raw file to NCA. If there are other steps
75 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom 108 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
76 // parsing function. 109 // parsing function.
77 explicit RegisteredCache(VirtualDir dir, 110 explicit RegisteredCache(VirtualDir dir,
78 RegisteredCacheParsingFunction parsing_function = 111 ContentProviderParsingFunction parsing_function =
79 [](const VirtualFile& file, const NcaID& id) { return file; }); 112 [](const VirtualFile& file, const NcaID& id) { return file; });
80 ~RegisteredCache(); 113 ~RegisteredCache() override;
81 114
82 void Refresh(); 115 void Refresh() override;
83 116
84 bool HasEntry(u64 title_id, ContentRecordType type) const; 117 bool HasEntry(u64 title_id, ContentRecordType type) const override;
85 bool HasEntry(RegisteredCacheEntry entry) const;
86 118
87 std::optional<u32> GetEntryVersion(u64 title_id) const; 119 std::optional<u32> GetEntryVersion(u64 title_id) const override;
88 120
89 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; 121 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
90 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
91 122
92 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 123 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
93 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
94 124
95 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 125 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
96 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
97 126
98 std::vector<RegisteredCacheEntry> ListEntries() const;
99 // If a parameter is not std::nullopt, it will be filtered for from all entries. 127 // If a parameter is not std::nullopt, it will be filtered for from all entries.
100 std::vector<RegisteredCacheEntry> ListEntriesFilter( 128 std::vector<ContentProviderEntry> ListEntriesFilter(
101 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, 129 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
102 std::optional<u64> title_id = {}) const; 130 std::optional<u64> title_id = {}) const override;
103 131
104 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure 132 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
105 // there is a meta NCA and all of them are accessible. 133 // there is a meta NCA and all of them are accessible.
@@ -131,46 +159,70 @@ private:
131 bool RawInstallYuzuMeta(const CNMT& cnmt); 159 bool RawInstallYuzuMeta(const CNMT& cnmt);
132 160
133 VirtualDir dir; 161 VirtualDir dir;
134 RegisteredCacheParsingFunction parser; 162 ContentProviderParsingFunction parser;
135 Core::Crypto::KeyManager keys;
136 163
137 // maps tid -> NcaID of meta 164 // maps tid -> NcaID of meta
138 boost::container::flat_map<u64, NcaID> meta_id; 165 std::map<u64, NcaID> meta_id;
139 // maps tid -> meta 166 // maps tid -> meta
140 boost::container::flat_map<u64, CNMT> meta; 167 std::map<u64, CNMT> meta;
141 // maps tid -> meta for CNMT in yuzu_meta 168 // maps tid -> meta for CNMT in yuzu_meta
142 boost::container::flat_map<u64, CNMT> yuzu_meta; 169 std::map<u64, CNMT> yuzu_meta;
143}; 170};
144 171
145// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. 172enum class ContentProviderUnionSlot {
146class RegisteredCacheUnion { 173 SysNAND, ///< System NAND
147public: 174 UserNAND, ///< User NAND
148 explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches); 175 SDMC, ///< SD Card
149 176 FrontendManual, ///< Frontend-defined game list or similar
150 void Refresh(); 177};
151
152 bool HasEntry(u64 title_id, ContentRecordType type) const;
153 bool HasEntry(RegisteredCacheEntry entry) const;
154
155 std::optional<u32> GetEntryVersion(u64 title_id) const;
156
157 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
158 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
159
160 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
161 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
162
163 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
164 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
165 178
166 std::vector<RegisteredCacheEntry> ListEntries() const; 179// Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface.
167 // If a parameter is not std::nullopt, it will be filtered for from all entries. 180class ContentProviderUnion : public ContentProvider {
168 std::vector<RegisteredCacheEntry> ListEntriesFilter( 181public:
182 ~ContentProviderUnion() override;
183
184 void SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider);
185 void ClearSlot(ContentProviderUnionSlot slot);
186
187 void Refresh() override;
188 bool HasEntry(u64 title_id, ContentRecordType type) const override;
189 std::optional<u32> GetEntryVersion(u64 title_id) const override;
190 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
191 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
192 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
193 std::vector<ContentProviderEntry> ListEntriesFilter(
194 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
195 std::optional<u64> title_id) const override;
196
197 std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> ListEntriesFilterOrigin(
198 std::optional<ContentProviderUnionSlot> origin = {},
169 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, 199 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
170 std::optional<u64> title_id = {}) const; 200 std::optional<u64> title_id = {}) const;
171 201
172private: 202private:
173 std::vector<RegisteredCache*> caches; 203 std::map<ContentProviderUnionSlot, ContentProvider*> providers;
204};
205
206class ManualContentProvider : public ContentProvider {
207public:
208 ~ManualContentProvider() override;
209
210 void AddEntry(TitleType title_type, ContentRecordType content_type, u64 title_id,
211 VirtualFile file);
212 void ClearAllEntries();
213
214 void Refresh() override;
215 bool HasEntry(u64 title_id, ContentRecordType type) const override;
216 std::optional<u32> GetEntryVersion(u64 title_id) const override;
217 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
218 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
219 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
220 std::vector<ContentProviderEntry> ListEntriesFilter(
221 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
222 std::optional<u64> title_id) const override;
223
224private:
225 std::map<std::tuple<TitleType, ContentRecordType, u64>, VirtualFile> entries;
174}; 226};
175 227
176} // namespace FileSys 228} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index 6ad1e4f86..b2ccb2926 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -48,7 +48,7 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
48 48
49 switch (storage) { 49 switch (storage) {
50 case StorageId::None: 50 case StorageId::None:
51 res = Service::FileSystem::GetUnionContents().GetEntry(title_id, type); 51 res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
52 break; 52 break;
53 case StorageId::NandSystem: 53 case StorageId::NandSystem:
54 res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); 54 res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 1913dc956..7974b031d 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -16,8 +16,10 @@ namespace FileSys {
16constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; 16constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
17 17
18std::string SaveDataDescriptor::DebugInfo() const { 18std::string SaveDataDescriptor::DebugInfo() const {
19 return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]", 19 return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, "
20 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); 20 "rank={}, index={}]",
21 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
22 static_cast<u8>(rank), index);
21} 23}
22 24
23SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { 25SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
@@ -28,7 +30,7 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save
28 30
29SaveDataFactory::~SaveDataFactory() = default; 31SaveDataFactory::~SaveDataFactory() = default;
30 32
31ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { 33ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
32 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { 34 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
33 if (meta.zero_1 != 0) { 35 if (meta.zero_1 != 0) {
34 LOG_WARNING(Service_FS, 36 LOG_WARNING(Service_FS,
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index 3a1caf292..b73654571 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -32,12 +32,19 @@ enum class SaveDataType : u8 {
32 CacheStorage = 5, 32 CacheStorage = 5,
33}; 33};
34 34
35enum class SaveDataRank : u8 {
36 Primary,
37 Secondary,
38};
39
35struct SaveDataDescriptor { 40struct SaveDataDescriptor {
36 u64_le title_id; 41 u64_le title_id;
37 u128 user_id; 42 u128 user_id;
38 u64_le save_id; 43 u64_le save_id;
39 SaveDataType type; 44 SaveDataType type;
40 INSERT_PADDING_BYTES(7); 45 SaveDataRank rank;
46 u16_le index;
47 INSERT_PADDING_BYTES(4);
41 u64_le zero_1; 48 u64_le zero_1;
42 u64_le zero_2; 49 u64_le zero_2;
43 u64_le zero_3; 50 u64_le zero_3;
@@ -57,7 +64,7 @@ public:
57 explicit SaveDataFactory(VirtualDir dir); 64 explicit SaveDataFactory(VirtualDir dir);
58 ~SaveDataFactory(); 65 ~SaveDataFactory();
59 66
60 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); 67 ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
61 68
62 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; 69 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
63 70
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index e1a4210db..c69caae0f 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -143,11 +143,12 @@ std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const {
143 return out; 143 return out;
144} 144}
145 145
146std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const { 146std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>>
147NSP::GetNCAs() const {
147 return ncas; 148 return ncas;
148} 149}
149 150
150std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const { 151std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType title_type) const {
151 if (extracted) 152 if (extracted)
152 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 153 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
153 154
@@ -155,14 +156,14 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
155 if (title_id_iter == ncas.end()) 156 if (title_id_iter == ncas.end())
156 return nullptr; 157 return nullptr;
157 158
158 const auto type_iter = title_id_iter->second.find(type); 159 const auto type_iter = title_id_iter->second.find({title_type, type});
159 if (type_iter == title_id_iter->second.end()) 160 if (type_iter == title_id_iter->second.end())
160 return nullptr; 161 return nullptr;
161 162
162 return type_iter->second; 163 return type_iter->second;
163} 164}
164 165
165VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const { 166VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const {
166 if (extracted) 167 if (extracted)
167 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 168 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
168 const auto nca = GetNCA(title_id, type); 169 const auto nca = GetNCA(title_id, type);
@@ -240,7 +241,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
240 const CNMT cnmt(inner_file); 241 const CNMT cnmt(inner_file);
241 auto& ncas_title = ncas[cnmt.GetTitleID()]; 242 auto& ncas_title = ncas[cnmt.GetTitleID()];
242 243
243 ncas_title[ContentRecordType::Meta] = nca; 244 ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca;
244 for (const auto& rec : cnmt.GetContentRecords()) { 245 for (const auto& rec : cnmt.GetContentRecords()) {
245 const auto id_string = Common::HexArrayToString(rec.nca_id, false); 246 const auto id_string = Common::HexArrayToString(rec.nca_id, false);
246 const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); 247 const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
@@ -258,7 +259,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
258 if (next_nca->GetStatus() == Loader::ResultStatus::Success || 259 if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
259 (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && 260 (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
260 (cnmt.GetTitleID() & 0x800) != 0)) { 261 (cnmt.GetTitleID() & 0x800) != 0)) {
261 ncas_title[rec.type] = std::move(next_nca); 262 ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca);
262 } 263 }
263 } 264 }
264 265
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 9a28ed5bb..ee9b6ce17 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -42,9 +42,12 @@ public:
42 // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML) 42 // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML)
43 std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const; 43 std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const;
44 std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const; 44 std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const;
45 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const; 45 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> GetNCAs()
46 std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const; 46 const;
47 VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const; 47 std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type,
48 TitleType title_type = TitleType::Application) const;
49 VirtualFile GetNCAFile(u64 title_id, ContentRecordType type,
50 TitleType title_type = TitleType::Application) const;
48 std::vector<Core::Crypto::Key128> GetTitlekey() const; 51 std::vector<Core::Crypto::Key128> GetTitlekey() const;
49 52
50 std::vector<VirtualFile> GetFiles() const override; 53 std::vector<VirtualFile> GetFiles() const override;
@@ -67,7 +70,7 @@ private:
67 70
68 std::shared_ptr<PartitionFilesystem> pfs; 71 std::shared_ptr<PartitionFilesystem> pfs;
69 // Map title id -> {map type -> NCA} 72 // Map title id -> {map type -> NCA}
70 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas; 73 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
71 std::vector<VirtualFile> ticket_files; 74 std::vector<VirtualFile> ticket_files;
72 75
73 Core::Crypto::KeyManager keys; 76 Core::Crypto::KeyManager keys;
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index d0bcb4660..70a522556 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -13,6 +13,23 @@
13namespace Core::Frontend { 13namespace Core::Frontend {
14 14
15/** 15/**
16 * Represents a graphics context that can be used for background computation or drawing. If the
17 * graphics backend doesn't require the context, then the implementation of these methods can be
18 * stubs
19 */
20class GraphicsContext {
21public:
22 /// Makes the graphics context current for the caller thread
23 virtual void MakeCurrent() = 0;
24
25 /// Releases (dunno if this is the "right" word) the context from the caller thread
26 virtual void DoneCurrent() = 0;
27
28 /// Swap buffers to display the next frame
29 virtual void SwapBuffers() = 0;
30};
31
32/**
16 * Abstraction class used to provide an interface between emulation code and the frontend 33 * Abstraction class used to provide an interface between emulation code and the frontend
17 * (e.g. SDL, QGLWidget, GLFW, etc...). 34 * (e.g. SDL, QGLWidget, GLFW, etc...).
18 * 35 *
@@ -30,7 +47,7 @@ namespace Core::Frontend {
30 * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please 47 * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
31 * re-read the upper points again and think about it if you don't see this. 48 * re-read the upper points again and think about it if you don't see this.
32 */ 49 */
33class EmuWindow { 50class EmuWindow : public GraphicsContext {
34public: 51public:
35 /// Data structure to store emuwindow configuration 52 /// Data structure to store emuwindow configuration
36 struct WindowConfig { 53 struct WindowConfig {
@@ -40,17 +57,21 @@ public:
40 std::pair<unsigned, unsigned> min_client_area_size; 57 std::pair<unsigned, unsigned> min_client_area_size;
41 }; 58 };
42 59
43 /// Swap buffers to display the next frame
44 virtual void SwapBuffers() = 0;
45
46 /// Polls window events 60 /// Polls window events
47 virtual void PollEvents() = 0; 61 virtual void PollEvents() = 0;
48 62
49 /// Makes the graphics context current for the caller thread 63 /**
50 virtual void MakeCurrent() = 0; 64 * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
51 65 * context can be used from other threads for background graphics computation. If the frontend
52 /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread 66 * is using a graphics backend that doesn't need anything specific to run on a different thread,
53 virtual void DoneCurrent() = 0; 67 * then it can use a stubbed implemenation for GraphicsContext.
68 *
69 * If the return value is null, then the core should assume that the frontend cannot provide a
70 * Shared Context
71 */
72 virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
73 return nullptr;
74 }
54 75
55 /** 76 /**
56 * Signal that a touch pressed event has occurred (e.g. mouse click pressed) 77 * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index dafb32aae..afa812598 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -1030,7 +1030,7 @@ static void Step() {
1030 1030
1031/// Tell the CPU if we hit a memory breakpoint. 1031/// Tell the CPU if we hit a memory breakpoint.
1032bool IsMemoryBreak() { 1032bool IsMemoryBreak() {
1033 if (IsConnected()) { 1033 if (!IsConnected()) {
1034 return false; 1034 return false;
1035 } 1035 }
1036 1036
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 68406eb63..ac0e1d796 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -139,10 +139,8 @@ public:
139 context->AddDomainObject(std::move(iface)); 139 context->AddDomainObject(std::move(iface));
140 } else { 140 } else {
141 auto& kernel = Core::System::GetInstance().Kernel(); 141 auto& kernel = Core::System::GetInstance().Kernel();
142 auto sessions = 142 auto [server, client] =
143 Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName()); 143 Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName());
144 auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions);
145 auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions);
146 iface->ClientConnected(server); 144 iface->ClientConnected(server);
147 context->AddMoveObject(std::move(client)); 145 context->AddMoveObject(std::move(client));
148 } 146 }
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index aa432658e..744b1697d 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -2,8 +2,6 @@
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 <tuple>
6
7#include "core/hle/kernel/client_port.h" 5#include "core/hle/kernel/client_port.h"
8#include "core/hle/kernel/client_session.h" 6#include "core/hle/kernel/client_session.h"
9#include "core/hle/kernel/errors.h" 7#include "core/hle/kernel/errors.h"
@@ -31,18 +29,18 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
31 active_sessions++; 29 active_sessions++;
32 30
33 // Create a new session pair, let the created sessions inherit the parent port's HLE handler. 31 // Create a new session pair, let the created sessions inherit the parent port's HLE handler.
34 auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this); 32 auto [server, client] = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
35 33
36 if (server_port->HasHLEHandler()) { 34 if (server_port->HasHLEHandler()) {
37 server_port->GetHLEHandler()->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); 35 server_port->GetHLEHandler()->ClientConnected(server);
38 } else { 36 } else {
39 server_port->AppendPendingSession(std::get<SharedPtr<ServerSession>>(sessions)); 37 server_port->AppendPendingSession(server);
40 } 38 }
41 39
42 // Wake the threads waiting on the ServerPort 40 // Wake the threads waiting on the ServerPort
43 server_port->WakeupAllWaitingThreads(); 41 server_port->WakeupAllWaitingThreads();
44 42
45 return MakeResult(std::get<SharedPtr<ClientSession>>(sessions)); 43 return MakeResult(client);
46} 44}
47 45
48void ClientPort::ConnectionClosed() { 46void ClientPort::ConnectionClosed() {
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
index 6cd607206..4921ad4f0 100644
--- a/src/core/hle/kernel/client_port.h
+++ b/src/core/hle/kernel/client_port.h
@@ -25,7 +25,7 @@ public:
25 return name; 25 return name;
26 } 26 }
27 27
28 static const HandleType HANDLE_TYPE = HandleType::ClientPort; 28 static constexpr HandleType HANDLE_TYPE = HandleType::ClientPort;
29 HandleType GetHandleType() const override { 29 HandleType GetHandleType() const override {
30 return HANDLE_TYPE; 30 return HANDLE_TYPE;
31 } 31 }
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index b1f39aad7..09cdff588 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -29,7 +29,7 @@ public:
29 return name; 29 return name;
30 } 30 }
31 31
32 static const HandleType HANDLE_TYPE = HandleType::ClientSession; 32 static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession;
33 HandleType GetHandleType() const override { 33 HandleType GetHandleType() const override {
34 return HANDLE_TYPE; 34 return HANDLE_TYPE;
35 } 35 }
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 3f14bfa86..4d58e7c69 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -21,6 +21,7 @@
21#include "core/hle/kernel/thread.h" 21#include "core/hle/kernel/thread.h"
22#include "core/hle/lock.h" 22#include "core/hle/lock.h"
23#include "core/hle/result.h" 23#include "core/hle/result.h"
24#include "core/memory.h"
24 25
25namespace Kernel { 26namespace Kernel {
26 27
@@ -181,6 +182,7 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
181 182
182void KernelCore::MakeCurrentProcess(Process* process) { 183void KernelCore::MakeCurrentProcess(Process* process) {
183 impl->current_process = process; 184 impl->current_process = process;
185 Memory::SetCurrentPageTable(&process->VMManager().page_table);
184} 186}
185 187
186Process* KernelCore::CurrentProcess() { 188Process* KernelCore::CurrentProcess() {
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index 217144efc..10431e94c 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -24,7 +24,6 @@ bool Object::IsWaitable() const {
24 case HandleType::WritableEvent: 24 case HandleType::WritableEvent:
25 case HandleType::SharedMemory: 25 case HandleType::SharedMemory:
26 case HandleType::TransferMemory: 26 case HandleType::TransferMemory:
27 case HandleType::AddressArbiter:
28 case HandleType::ResourceLimit: 27 case HandleType::ResourceLimit:
29 case HandleType::ClientPort: 28 case HandleType::ClientPort:
30 case HandleType::ClientSession: 29 case HandleType::ClientSession:
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index 3f6baa094..332876c27 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -25,7 +25,6 @@ enum class HandleType : u32 {
25 TransferMemory, 25 TransferMemory,
26 Thread, 26 Thread,
27 Process, 27 Process,
28 AddressArbiter,
29 ResourceLimit, 28 ResourceLimit,
30 ClientPort, 29 ClientPort,
31 ServerPort, 30 ServerPort,
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 041267318..4e94048da 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -32,9 +32,6 @@ namespace {
32 * @param priority The priority to give the main thread 32 * @param priority The priority to give the main thread
33 */ 33 */
34void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) { 34void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
35 // Setup page table so we can write to memory
36 Memory::SetCurrentPageTable(&owner_process.VMManager().page_table);
37
38 // Initialize new "main" thread 35 // Initialize new "main" thread
39 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); 36 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
40 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, 37 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
@@ -109,6 +106,8 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
109 is_64bit_process = metadata.Is64BitProgram(); 106 is_64bit_process = metadata.Is64BitProgram();
110 107
111 vm_manager.Reset(metadata.GetAddressSpaceType()); 108 vm_manager.Reset(metadata.GetAddressSpaceType());
109 // Ensure that the potentially resized page table is seen by CPU backends.
110 Memory::SetCurrentPageTable(&vm_manager.page_table);
112 111
113 const auto& caps = metadata.GetKernelCapabilities(); 112 const auto& caps = metadata.GetKernelCapabilities();
114 const auto capability_init_result = 113 const auto capability_init_result =
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index f060f2a3b..dda52f4c0 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -85,7 +85,7 @@ public:
85 return name; 85 return name;
86 } 86 }
87 87
88 static const HandleType HANDLE_TYPE = HandleType::Process; 88 static constexpr HandleType HANDLE_TYPE = HandleType::Process;
89 HandleType GetHandleType() const override { 89 HandleType GetHandleType() const override {
90 return HANDLE_TYPE; 90 return HANDLE_TYPE;
91 } 91 }
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h
index 2eb9dcbb7..84215f572 100644
--- a/src/core/hle/kernel/readable_event.h
+++ b/src/core/hle/kernel/readable_event.h
@@ -31,7 +31,7 @@ public:
31 return reset_type; 31 return reset_type;
32 } 32 }
33 33
34 static const HandleType HANDLE_TYPE = HandleType::ReadableEvent; 34 static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
35 HandleType GetHandleType() const override { 35 HandleType GetHandleType() const override {
36 return HANDLE_TYPE; 36 return HANDLE_TYPE;
37 } 37 }
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 70e09858a..2613a6bb5 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -41,7 +41,7 @@ public:
41 return GetTypeName(); 41 return GetTypeName();
42 } 42 }
43 43
44 static const HandleType HANDLE_TYPE = HandleType::ResourceLimit; 44 static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
45 HandleType GetHandleType() const override { 45 HandleType GetHandleType() const override {
46 return HANDLE_TYPE; 46 return HANDLE_TYPE;
47 } 47 }
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index ac501bf7f..e8447b69a 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -101,7 +101,6 @@ void Scheduler::SwitchContext(Thread* new_thread) {
101 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 101 auto* const thread_owner_process = current_thread->GetOwnerProcess();
102 if (previous_process != thread_owner_process) { 102 if (previous_process != thread_owner_process) {
103 system.Kernel().MakeCurrentProcess(thread_owner_process); 103 system.Kernel().MakeCurrentProcess(thread_owner_process);
104 Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
105 } 104 }
106 105
107 cpu_core.LoadContext(new_thread->GetContext()); 106 cpu_core.LoadContext(new_thread->GetContext());
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 708fdf9e1..02e7c60e6 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -39,9 +39,8 @@ void ServerPort::Acquire(Thread* thread) {
39 ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); 39 ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
40} 40}
41 41
42std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( 42ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions,
43 KernelCore& kernel, u32 max_sessions, std::string name) { 43 std::string name) {
44
45 SharedPtr<ServerPort> server_port(new ServerPort(kernel)); 44 SharedPtr<ServerPort> server_port(new ServerPort(kernel));
46 SharedPtr<ClientPort> client_port(new ClientPort(kernel)); 45 SharedPtr<ClientPort> client_port(new ClientPort(kernel));
47 46
@@ -51,7 +50,7 @@ std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortP
51 client_port->max_sessions = max_sessions; 50 client_port->max_sessions = max_sessions;
52 client_port->active_sessions = 0; 51 client_port->active_sessions = 0;
53 52
54 return std::make_tuple(std::move(server_port), std::move(client_port)); 53 return std::make_pair(std::move(server_port), std::move(client_port));
55} 54}
56 55
57} // namespace Kernel 56} // namespace Kernel
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index 76293cb8b..dc88a1ebd 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -6,7 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <tuple> 9#include <utility>
10#include <vector> 10#include <vector>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/hle/kernel/object.h" 12#include "core/hle/kernel/object.h"
@@ -23,6 +23,7 @@ class SessionRequestHandler;
23class ServerPort final : public WaitObject { 23class ServerPort final : public WaitObject {
24public: 24public:
25 using HLEHandler = std::shared_ptr<SessionRequestHandler>; 25 using HLEHandler = std::shared_ptr<SessionRequestHandler>;
26 using PortPair = std::pair<SharedPtr<ServerPort>, SharedPtr<ClientPort>>;
26 27
27 /** 28 /**
28 * Creates a pair of ServerPort and an associated ClientPort. 29 * Creates a pair of ServerPort and an associated ClientPort.
@@ -32,8 +33,8 @@ public:
32 * @param name Optional name of the ports 33 * @param name Optional name of the ports
33 * @return The created port tuple 34 * @return The created port tuple
34 */ 35 */
35 static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair( 36 static PortPair CreatePortPair(KernelCore& kernel, u32 max_sessions,
36 KernelCore& kernel, u32 max_sessions, std::string name = "UnknownPort"); 37 std::string name = "UnknownPort");
37 38
38 std::string GetTypeName() const override { 39 std::string GetTypeName() const override {
39 return "ServerPort"; 40 return "ServerPort";
@@ -42,7 +43,7 @@ public:
42 return name; 43 return name;
43 } 44 }
44 45
45 static const HandleType HANDLE_TYPE = HandleType::ServerPort; 46 static constexpr HandleType HANDLE_TYPE = HandleType::ServerPort;
46 HandleType GetHandleType() const override { 47 HandleType GetHandleType() const override {
47 return HANDLE_TYPE; 48 return HANDLE_TYPE;
48 } 49 }
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 40cec143e..696a82cd9 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -28,11 +28,9 @@ ServerSession::~ServerSession() {
28 // the emulated application. 28 // the emulated application.
29 29
30 // Decrease the port's connection count. 30 // Decrease the port's connection count.
31 if (parent->port) 31 if (parent->port) {
32 parent->port->ConnectionClosed(); 32 parent->port->ConnectionClosed();
33 33 }
34 // TODO(Subv): Wake up all the ClientSession's waiting threads and set
35 // the SendSyncRequest result to 0xC920181A.
36 34
37 parent->server = nullptr; 35 parent->server = nullptr;
38} 36}
@@ -74,9 +72,6 @@ void ServerSession::ClientDisconnected() {
74 handler->ClientDisconnected(this); 72 handler->ClientDisconnected(this);
75 } 73 }
76 74
77 // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
78 // their WaitSynchronization result to 0xC920181A.
79
80 // Clean up the list of client threads with pending requests, they are unneeded now that the 75 // Clean up the list of client threads with pending requests, they are unneeded now that the
81 // client endpoint is closed. 76 // client endpoint is closed.
82 pending_requesting_threads.clear(); 77 pending_requesting_threads.clear();
@@ -204,6 +199,6 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel,
204 client_session->parent = parent; 199 client_session->parent = parent;
205 server_session->parent = parent; 200 server_session->parent = parent;
206 201
207 return std::make_tuple(std::move(server_session), std::move(client_session)); 202 return std::make_pair(std::move(server_session), std::move(client_session));
208} 203}
209} // namespace Kernel 204} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 79b84bade..738df30f8 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <utility>
9#include <vector> 10#include <vector>
10 11
11#include "core/hle/kernel/object.h" 12#include "core/hle/kernel/object.h"
@@ -41,7 +42,11 @@ public:
41 return "ServerSession"; 42 return "ServerSession";
42 } 43 }
43 44
44 static const HandleType HANDLE_TYPE = HandleType::ServerSession; 45 std::string GetName() const override {
46 return name;
47 }
48
49 static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession;
45 HandleType GetHandleType() const override { 50 HandleType GetHandleType() const override {
46 return HANDLE_TYPE; 51 return HANDLE_TYPE;
47 } 52 }
@@ -54,7 +59,7 @@ public:
54 return parent.get(); 59 return parent.get();
55 } 60 }
56 61
57 using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>; 62 using SessionPair = std::pair<SharedPtr<ServerSession>, SharedPtr<ClientSession>>;
58 63
59 /** 64 /**
60 * Creates a pair of ServerSession and an associated ClientSession. 65 * Creates a pair of ServerSession and an associated ClientSession.
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 37e18c443..c2b6155e1 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -76,7 +76,7 @@ public:
76 return name; 76 return name;
77 } 77 }
78 78
79 static const HandleType HANDLE_TYPE = HandleType::SharedMemory; 79 static constexpr HandleType HANDLE_TYPE = HandleType::SharedMemory;
80 HandleType GetHandleType() const override { 80 HandleType GetHandleType() const override {
81 return HANDLE_TYPE; 81 return HANDLE_TYPE;
82 } 82 }
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index ab10db3df..d48a2203a 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -131,16 +131,15 @@ enum class ResourceLimitValueType {
131 LimitValue, 131 LimitValue,
132}; 132};
133 133
134ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_type, 134ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit,
135 ResourceLimitValueType value_type) { 135 u32 resource_type, ResourceLimitValueType value_type) {
136 const auto type = static_cast<ResourceType>(resource_type); 136 const auto type = static_cast<ResourceType>(resource_type);
137 if (!IsValidResourceType(type)) { 137 if (!IsValidResourceType(type)) {
138 LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); 138 LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
139 return ERR_INVALID_ENUM_VALUE; 139 return ERR_INVALID_ENUM_VALUE;
140 } 140 }
141 141
142 const auto& kernel = Core::System::GetInstance().Kernel(); 142 const auto* const current_process = system.Kernel().CurrentProcess();
143 const auto* const current_process = kernel.CurrentProcess();
144 ASSERT(current_process != nullptr); 143 ASSERT(current_process != nullptr);
145 144
146 const auto resource_limit_object = 145 const auto resource_limit_object =
@@ -160,7 +159,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_ty
160} // Anonymous namespace 159} // Anonymous namespace
161 160
162/// Set the process heap to a given Size. It can both extend and shrink the heap. 161/// Set the process heap to a given Size. It can both extend and shrink the heap.
163static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { 162static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) {
164 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); 163 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
165 164
166 // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. 165 // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
@@ -175,7 +174,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
175 return ERR_INVALID_SIZE; 174 return ERR_INVALID_SIZE;
176 } 175 }
177 176
178 auto& vm_manager = Core::System::GetInstance().Kernel().CurrentProcess()->VMManager(); 177 auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
179 const auto alloc_result = vm_manager.SetHeapSize(heap_size); 178 const auto alloc_result = vm_manager.SetHeapSize(heap_size);
180 if (alloc_result.Failed()) { 179 if (alloc_result.Failed()) {
181 return alloc_result.Code(); 180 return alloc_result.Code();
@@ -185,7 +184,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
185 return RESULT_SUCCESS; 184 return RESULT_SUCCESS;
186} 185}
187 186
188static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { 187static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) {
189 LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot); 188 LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
190 189
191 if (!Common::Is4KBAligned(addr)) { 190 if (!Common::Is4KBAligned(addr)) {
@@ -217,7 +216,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
217 return ERR_INVALID_MEMORY_PERMISSIONS; 216 return ERR_INVALID_MEMORY_PERMISSIONS;
218 } 217 }
219 218
220 auto* const current_process = Core::CurrentProcess(); 219 auto* const current_process = system.Kernel().CurrentProcess();
221 auto& vm_manager = current_process->VMManager(); 220 auto& vm_manager = current_process->VMManager();
222 221
223 if (!vm_manager.IsWithinAddressSpace(addr, size)) { 222 if (!vm_manager.IsWithinAddressSpace(addr, size)) {
@@ -242,7 +241,8 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
242 return vm_manager.ReprotectRange(addr, size, converted_permissions); 241 return vm_manager.ReprotectRange(addr, size, converted_permissions);
243} 242}
244 243
245static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) { 244static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
245 u32 attribute) {
246 LOG_DEBUG(Kernel_SVC, 246 LOG_DEBUG(Kernel_SVC,
247 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, 247 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
248 size, mask, attribute); 248 size, mask, attribute);
@@ -280,7 +280,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr
280 return ERR_INVALID_COMBINATION; 280 return ERR_INVALID_COMBINATION;
281 } 281 }
282 282
283 auto& vm_manager = Core::CurrentProcess()->VMManager(); 283 auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
284 if (!vm_manager.IsWithinAddressSpace(address, size)) { 284 if (!vm_manager.IsWithinAddressSpace(address, size)) {
285 LOG_ERROR(Kernel_SVC, 285 LOG_ERROR(Kernel_SVC,
286 "Given address (0x{:016X}) is outside the bounds of the address space.", address); 286 "Given address (0x{:016X}) is outside the bounds of the address space.", address);
@@ -291,11 +291,11 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr
291} 291}
292 292
293/// Maps a memory range into a different range. 293/// Maps a memory range into a different range.
294static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 294static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
295 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 295 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
296 src_addr, size); 296 src_addr, size);
297 297
298 auto& vm_manager = Core::CurrentProcess()->VMManager(); 298 auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
299 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); 299 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
300 300
301 if (result.IsError()) { 301 if (result.IsError()) {
@@ -306,11 +306,11 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
306} 306}
307 307
308/// Unmaps a region that was previously mapped with svcMapMemory 308/// Unmaps a region that was previously mapped with svcMapMemory
309static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 309static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
310 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 310 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
311 src_addr, size); 311 src_addr, size);
312 312
313 auto& vm_manager = Core::CurrentProcess()->VMManager(); 313 auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
314 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); 314 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
315 315
316 if (result.IsError()) { 316 if (result.IsError()) {
@@ -321,7 +321,8 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
321} 321}
322 322
323/// Connect to an OS service given the port name, returns the handle to the port to out 323/// Connect to an OS service given the port name, returns the handle to the port to out
324static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) { 324static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
325 VAddr port_name_address) {
325 if (!Memory::IsValidVirtualAddress(port_name_address)) { 326 if (!Memory::IsValidVirtualAddress(port_name_address)) {
326 LOG_ERROR(Kernel_SVC, 327 LOG_ERROR(Kernel_SVC,
327 "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", 328 "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
@@ -340,8 +341,8 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
340 341
341 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); 342 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
342 343
343 auto& kernel = Core::System::GetInstance().Kernel(); 344 auto& kernel = system.Kernel();
344 auto it = kernel.FindNamedPort(port_name); 345 const auto it = kernel.FindNamedPort(port_name);
345 if (!kernel.IsValidNamedPort(it)) { 346 if (!kernel.IsValidNamedPort(it)) {
346 LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); 347 LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
347 return ERR_NOT_FOUND; 348 return ERR_NOT_FOUND;
@@ -353,14 +354,14 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
353 CASCADE_RESULT(client_session, client_port->Connect()); 354 CASCADE_RESULT(client_session, client_port->Connect());
354 355
355 // Return the client session 356 // Return the client session
356 auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 357 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
357 CASCADE_RESULT(*out_handle, handle_table.Create(client_session)); 358 CASCADE_RESULT(*out_handle, handle_table.Create(client_session));
358 return RESULT_SUCCESS; 359 return RESULT_SUCCESS;
359} 360}
360 361
361/// Makes a blocking IPC call to an OS service. 362/// Makes a blocking IPC call to an OS service.
362static ResultCode SendSyncRequest(Handle handle) { 363static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
363 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 364 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
364 SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle); 365 SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle);
365 if (!session) { 366 if (!session) {
366 LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); 367 LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
@@ -369,18 +370,18 @@ static ResultCode SendSyncRequest(Handle handle) {
369 370
370 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); 371 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
371 372
372 Core::System::GetInstance().PrepareReschedule(); 373 system.PrepareReschedule();
373 374
374 // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server 375 // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server
375 // responds and cause a reschedule. 376 // responds and cause a reschedule.
376 return session->SendSyncRequest(GetCurrentThread()); 377 return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread());
377} 378}
378 379
379/// Get the ID for the specified thread. 380/// Get the ID for the specified thread.
380static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) { 381static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) {
381 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 382 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
382 383
383 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 384 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
384 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); 385 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
385 if (!thread) { 386 if (!thread) {
386 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle); 387 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle);
@@ -392,10 +393,10 @@ static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) {
392} 393}
393 394
394/// Gets the ID of the specified process or a specified thread's owning process. 395/// Gets the ID of the specified process or a specified thread's owning process.
395static ResultCode GetProcessId(u64* process_id, Handle handle) { 396static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) {
396 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); 397 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
397 398
398 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 399 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
399 const SharedPtr<Process> process = handle_table.Get<Process>(handle); 400 const SharedPtr<Process> process = handle_table.Get<Process>(handle);
400 if (process) { 401 if (process) {
401 *process_id = process->GetProcessID(); 402 *process_id = process->GetProcessID();
@@ -437,8 +438,8 @@ static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thr
437}; 438};
438 439
439/// Wait for the given handles to synchronize, timeout after the specified nanoseconds 440/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
440static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count, 441static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address,
441 s64 nano_seconds) { 442 u64 handle_count, s64 nano_seconds) {
442 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", 443 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
443 handles_address, handle_count, nano_seconds); 444 handles_address, handle_count, nano_seconds);
444 445
@@ -457,11 +458,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
457 return ERR_OUT_OF_RANGE; 458 return ERR_OUT_OF_RANGE;
458 } 459 }
459 460
460 auto* const thread = GetCurrentThread(); 461 auto* const thread = system.CurrentScheduler().GetCurrentThread();
461 462
462 using ObjectPtr = Thread::ThreadWaitObjects::value_type; 463 using ObjectPtr = Thread::ThreadWaitObjects::value_type;
463 Thread::ThreadWaitObjects objects(handle_count); 464 Thread::ThreadWaitObjects objects(handle_count);
464 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 465 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
465 466
466 for (u64 i = 0; i < handle_count; ++i) { 467 for (u64 i = 0; i < handle_count; ++i) {
467 const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); 468 const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
@@ -507,16 +508,16 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
507 thread->WakeAfterDelay(nano_seconds); 508 thread->WakeAfterDelay(nano_seconds);
508 thread->SetWakeupCallback(DefaultThreadWakeupCallback); 509 thread->SetWakeupCallback(DefaultThreadWakeupCallback);
509 510
510 Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); 511 system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
511 512
512 return RESULT_TIMEOUT; 513 return RESULT_TIMEOUT;
513} 514}
514 515
515/// Resumes a thread waiting on WaitSynchronization 516/// Resumes a thread waiting on WaitSynchronization
516static ResultCode CancelSynchronization(Handle thread_handle) { 517static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
517 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); 518 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
518 519
519 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 520 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
520 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); 521 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
521 if (!thread) { 522 if (!thread) {
522 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", 523 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
@@ -531,8 +532,8 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
531} 532}
532 533
533/// Attempts to locks a mutex, creating it if it does not already exist 534/// Attempts to locks a mutex, creating it if it does not already exist
534static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, 535static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle,
535 Handle requesting_thread_handle) { 536 VAddr mutex_addr, Handle requesting_thread_handle) {
536 LOG_TRACE(Kernel_SVC, 537 LOG_TRACE(Kernel_SVC,
537 "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " 538 "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, "
538 "requesting_current_thread_handle=0x{:08X}", 539 "requesting_current_thread_handle=0x{:08X}",
@@ -549,13 +550,13 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
549 return ERR_INVALID_ADDRESS; 550 return ERR_INVALID_ADDRESS;
550 } 551 }
551 552
552 auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); 553 auto* const current_process = system.Kernel().CurrentProcess();
553 return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, 554 return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
554 requesting_thread_handle); 555 requesting_thread_handle);
555} 556}
556 557
557/// Unlock a mutex 558/// Unlock a mutex
558static ResultCode ArbitrateUnlock(VAddr mutex_addr) { 559static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
559 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); 560 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr);
560 561
561 if (Memory::IsKernelVirtualAddress(mutex_addr)) { 562 if (Memory::IsKernelVirtualAddress(mutex_addr)) {
@@ -569,7 +570,7 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
569 return ERR_INVALID_ADDRESS; 570 return ERR_INVALID_ADDRESS;
570 } 571 }
571 572
572 auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); 573 auto* const current_process = system.Kernel().CurrentProcess();
573 return current_process->GetMutex().Release(mutex_addr); 574 return current_process->GetMutex().Release(mutex_addr);
574} 575}
575 576
@@ -592,7 +593,7 @@ struct BreakReason {
592}; 593};
593 594
594/// Break program execution 595/// Break program execution
595static void Break(u32 reason, u64 info1, u64 info2) { 596static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
596 BreakReason break_reason{reason}; 597 BreakReason break_reason{reason};
597 bool has_dumped_buffer{}; 598 bool has_dumped_buffer{};
598 599
@@ -670,22 +671,24 @@ static void Break(u32 reason, u64 info1, u64 info2) {
670 Debug_Emulated, 671 Debug_Emulated,
671 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 672 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
672 reason, info1, info2); 673 reason, info1, info2);
674
673 handle_debug_buffer(info1, info2); 675 handle_debug_buffer(info1, info2);
674 Core::System::GetInstance() 676
675 .ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID())) 677 auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
676 .LogBacktrace(); 678 const auto thread_processor_id = current_thread->GetProcessorID();
679 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
677 ASSERT(false); 680 ASSERT(false);
678 681
679 Core::CurrentProcess()->PrepareForTermination(); 682 system.Kernel().CurrentProcess()->PrepareForTermination();
680 683
681 // Kill the current thread 684 // Kill the current thread
682 GetCurrentThread()->Stop(); 685 current_thread->Stop();
683 Core::System::GetInstance().PrepareReschedule(); 686 system.PrepareReschedule();
684 } 687 }
685} 688}
686 689
687/// Used to output a message on a debug hardware unit - does nothing on a retail unit 690/// Used to output a message on a debug hardware unit - does nothing on a retail unit
688static void OutputDebugString(VAddr address, u64 len) { 691static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) {
689 if (len == 0) { 692 if (len == 0) {
690 return; 693 return;
691 } 694 }
@@ -696,7 +699,8 @@ static void OutputDebugString(VAddr address, u64 len) {
696} 699}
697 700
698/// Gets system/memory information for the current process 701/// Gets system/memory information for the current process
699static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) { 702static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle,
703 u64 info_sub_id) {
700 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, 704 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
701 info_sub_id, handle); 705 info_sub_id, handle);
702 706
@@ -754,7 +758,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
754 return ERR_INVALID_ENUM_VALUE; 758 return ERR_INVALID_ENUM_VALUE;
755 } 759 }
756 760
757 const auto& current_process_handle_table = Core::CurrentProcess()->GetHandleTable(); 761 const auto& current_process_handle_table =
762 system.Kernel().CurrentProcess()->GetHandleTable();
758 const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle)); 763 const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle));
759 if (!process) { 764 if (!process) {
760 return ERR_INVALID_HANDLE; 765 return ERR_INVALID_HANDLE;
@@ -844,7 +849,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
844 return ERR_INVALID_COMBINATION; 849 return ERR_INVALID_COMBINATION;
845 } 850 }
846 851
847 Process* const current_process = Core::CurrentProcess(); 852 Process* const current_process = system.Kernel().CurrentProcess();
848 HandleTable& handle_table = current_process->GetHandleTable(); 853 HandleTable& handle_table = current_process->GetHandleTable();
849 const auto resource_limit = current_process->GetResourceLimit(); 854 const auto resource_limit = current_process->GetResourceLimit();
850 if (!resource_limit) { 855 if (!resource_limit) {
@@ -875,7 +880,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
875 return ERR_INVALID_COMBINATION; 880 return ERR_INVALID_COMBINATION;
876 } 881 }
877 882
878 *result = Core::CurrentProcess()->GetRandomEntropy(info_sub_id); 883 *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
879 return RESULT_SUCCESS; 884 return RESULT_SUCCESS;
880 885
881 case GetInfoType::PrivilegedProcessId: 886 case GetInfoType::PrivilegedProcessId:
@@ -892,15 +897,14 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
892 return ERR_INVALID_COMBINATION; 897 return ERR_INVALID_COMBINATION;
893 } 898 }
894 899
895 const auto thread = 900 const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<Thread>(
896 Core::CurrentProcess()->GetHandleTable().Get<Thread>(static_cast<Handle>(handle)); 901 static_cast<Handle>(handle));
897 if (!thread) { 902 if (!thread) {
898 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", 903 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
899 static_cast<Handle>(handle)); 904 static_cast<Handle>(handle));
900 return ERR_INVALID_HANDLE; 905 return ERR_INVALID_HANDLE;
901 } 906 }
902 907
903 const auto& system = Core::System::GetInstance();
904 const auto& core_timing = system.CoreTiming(); 908 const auto& core_timing = system.CoreTiming();
905 const auto& scheduler = system.CurrentScheduler(); 909 const auto& scheduler = system.CurrentScheduler();
906 const auto* const current_thread = scheduler.GetCurrentThread(); 910 const auto* const current_thread = scheduler.GetCurrentThread();
@@ -927,13 +931,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
927} 931}
928 932
929/// Sets the thread activity 933/// Sets the thread activity
930static ResultCode SetThreadActivity(Handle handle, u32 activity) { 934static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
931 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); 935 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
932 if (activity > static_cast<u32>(ThreadActivity::Paused)) { 936 if (activity > static_cast<u32>(ThreadActivity::Paused)) {
933 return ERR_INVALID_ENUM_VALUE; 937 return ERR_INVALID_ENUM_VALUE;
934 } 938 }
935 939
936 const auto* current_process = Core::CurrentProcess(); 940 const auto* current_process = system.Kernel().CurrentProcess();
937 const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); 941 const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
938 if (!thread) { 942 if (!thread) {
939 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); 943 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
@@ -950,7 +954,7 @@ static ResultCode SetThreadActivity(Handle handle, u32 activity) {
950 return ERR_INVALID_HANDLE; 954 return ERR_INVALID_HANDLE;
951 } 955 }
952 956
953 if (thread == GetCurrentThread()) { 957 if (thread == system.CurrentScheduler().GetCurrentThread()) {
954 LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); 958 LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
955 return ERR_BUSY; 959 return ERR_BUSY;
956 } 960 }
@@ -960,10 +964,10 @@ static ResultCode SetThreadActivity(Handle handle, u32 activity) {
960} 964}
961 965
962/// Gets the thread context 966/// Gets the thread context
963static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { 967static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) {
964 LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); 968 LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
965 969
966 const auto* current_process = Core::CurrentProcess(); 970 const auto* current_process = system.Kernel().CurrentProcess();
967 const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); 971 const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
968 if (!thread) { 972 if (!thread) {
969 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); 973 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
@@ -980,7 +984,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
980 return ERR_INVALID_HANDLE; 984 return ERR_INVALID_HANDLE;
981 } 985 }
982 986
983 if (thread == GetCurrentThread()) { 987 if (thread == system.CurrentScheduler().GetCurrentThread()) {
984 LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); 988 LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
985 return ERR_BUSY; 989 return ERR_BUSY;
986 } 990 }
@@ -1001,10 +1005,10 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
1001} 1005}
1002 1006
1003/// Gets the priority for the specified thread 1007/// Gets the priority for the specified thread
1004static ResultCode GetThreadPriority(u32* priority, Handle handle) { 1008static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) {
1005 LOG_TRACE(Kernel_SVC, "called"); 1009 LOG_TRACE(Kernel_SVC, "called");
1006 1010
1007 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1011 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1008 const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle); 1012 const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
1009 if (!thread) { 1013 if (!thread) {
1010 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); 1014 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
@@ -1016,7 +1020,7 @@ static ResultCode GetThreadPriority(u32* priority, Handle handle) {
1016} 1020}
1017 1021
1018/// Sets the priority for the specified thread 1022/// Sets the priority for the specified thread
1019static ResultCode SetThreadPriority(Handle handle, u32 priority) { 1023static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
1020 LOG_TRACE(Kernel_SVC, "called"); 1024 LOG_TRACE(Kernel_SVC, "called");
1021 1025
1022 if (priority > THREADPRIO_LOWEST) { 1026 if (priority > THREADPRIO_LOWEST) {
@@ -1027,7 +1031,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
1027 return ERR_INVALID_THREAD_PRIORITY; 1031 return ERR_INVALID_THREAD_PRIORITY;
1028 } 1032 }
1029 1033
1030 const auto* const current_process = Core::CurrentProcess(); 1034 const auto* const current_process = system.Kernel().CurrentProcess();
1031 1035
1032 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); 1036 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
1033 if (!thread) { 1037 if (!thread) {
@@ -1037,18 +1041,18 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
1037 1041
1038 thread->SetPriority(priority); 1042 thread->SetPriority(priority);
1039 1043
1040 Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); 1044 system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
1041 return RESULT_SUCCESS; 1045 return RESULT_SUCCESS;
1042} 1046}
1043 1047
1044/// Get which CPU core is executing the current thread 1048/// Get which CPU core is executing the current thread
1045static u32 GetCurrentProcessorNumber() { 1049static u32 GetCurrentProcessorNumber(Core::System& system) {
1046 LOG_TRACE(Kernel_SVC, "called"); 1050 LOG_TRACE(Kernel_SVC, "called");
1047 return GetCurrentThread()->GetProcessorID(); 1051 return system.CurrentScheduler().GetCurrentThread()->GetProcessorID();
1048} 1052}
1049 1053
1050static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size, 1054static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr,
1051 u32 permissions) { 1055 u64 size, u32 permissions) {
1052 LOG_TRACE(Kernel_SVC, 1056 LOG_TRACE(Kernel_SVC,
1053 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", 1057 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1054 shared_memory_handle, addr, size, permissions); 1058 shared_memory_handle, addr, size, permissions);
@@ -1082,7 +1086,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
1082 return ERR_INVALID_MEMORY_PERMISSIONS; 1086 return ERR_INVALID_MEMORY_PERMISSIONS;
1083 } 1087 }
1084 1088
1085 auto* const current_process = Core::CurrentProcess(); 1089 auto* const current_process = system.Kernel().CurrentProcess();
1086 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); 1090 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
1087 if (!shared_memory) { 1091 if (!shared_memory) {
1088 LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", 1092 LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
@@ -1100,7 +1104,8 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
1100 return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare); 1104 return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare);
1101} 1105}
1102 1106
1103static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 1107static ResultCode UnmapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr,
1108 u64 size) {
1104 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", 1109 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
1105 shared_memory_handle, addr, size); 1110 shared_memory_handle, addr, size);
1106 1111
@@ -1125,7 +1130,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
1125 return ERR_INVALID_ADDRESS_STATE; 1130 return ERR_INVALID_ADDRESS_STATE;
1126 } 1131 }
1127 1132
1128 auto* const current_process = Core::CurrentProcess(); 1133 auto* const current_process = system.Kernel().CurrentProcess();
1129 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); 1134 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
1130 if (!shared_memory) { 1135 if (!shared_memory) {
1131 LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", 1136 LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
@@ -1143,10 +1148,11 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
1143 return shared_memory->Unmap(*current_process, addr, size); 1148 return shared_memory->Unmap(*current_process, addr, size);
1144} 1149}
1145 1150
1146static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address, 1151static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
1147 Handle process_handle, VAddr address) { 1152 VAddr page_info_address, Handle process_handle,
1153 VAddr address) {
1148 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); 1154 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
1149 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1155 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1150 SharedPtr<Process> process = handle_table.Get<Process>(process_handle); 1156 SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
1151 if (!process) { 1157 if (!process) {
1152 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", 1158 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
@@ -1172,20 +1178,156 @@ static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_
1172 return RESULT_SUCCESS; 1178 return RESULT_SUCCESS;
1173} 1179}
1174 1180
1175static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address, 1181static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
1176 VAddr query_address) { 1182 VAddr page_info_address, VAddr query_address) {
1177 LOG_TRACE(Kernel_SVC, 1183 LOG_TRACE(Kernel_SVC,
1178 "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " 1184 "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
1179 "query_address=0x{:016X}", 1185 "query_address=0x{:016X}",
1180 memory_info_address, page_info_address, query_address); 1186 memory_info_address, page_info_address, query_address);
1181 1187
1182 return QueryProcessMemory(memory_info_address, page_info_address, CurrentProcess, 1188 return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess,
1183 query_address); 1189 query_address);
1184} 1190}
1185 1191
1192static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1193 u64 src_address, u64 size) {
1194 LOG_DEBUG(Kernel_SVC,
1195 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
1196 "src_address=0x{:016X}, size=0x{:016X}",
1197 process_handle, dst_address, src_address, size);
1198
1199 if (!Common::Is4KBAligned(src_address)) {
1200 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1201 src_address);
1202 return ERR_INVALID_ADDRESS;
1203 }
1204
1205 if (!Common::Is4KBAligned(dst_address)) {
1206 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1207 dst_address);
1208 return ERR_INVALID_ADDRESS;
1209 }
1210
1211 if (size == 0 || !Common::Is4KBAligned(size)) {
1212 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
1213 return ERR_INVALID_SIZE;
1214 }
1215
1216 if (!IsValidAddressRange(dst_address, size)) {
1217 LOG_ERROR(Kernel_SVC,
1218 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1219 "size=0x{:016X}).",
1220 dst_address, size);
1221 return ERR_INVALID_ADDRESS_STATE;
1222 }
1223
1224 if (!IsValidAddressRange(src_address, size)) {
1225 LOG_ERROR(Kernel_SVC,
1226 "Source address range overflows the address space (src_address=0x{:016X}, "
1227 "size=0x{:016X}).",
1228 src_address, size);
1229 return ERR_INVALID_ADDRESS_STATE;
1230 }
1231
1232 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1233 auto process = handle_table.Get<Process>(process_handle);
1234 if (!process) {
1235 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1236 process_handle);
1237 return ERR_INVALID_HANDLE;
1238 }
1239
1240 auto& vm_manager = process->VMManager();
1241 if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
1242 LOG_ERROR(Kernel_SVC,
1243 "Source address range is not within the address space (src_address=0x{:016X}, "
1244 "size=0x{:016X}).",
1245 src_address, size);
1246 return ERR_INVALID_ADDRESS_STATE;
1247 }
1248
1249 if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
1250 LOG_ERROR(Kernel_SVC,
1251 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1252 "size=0x{:016X}).",
1253 dst_address, size);
1254 return ERR_INVALID_MEMORY_RANGE;
1255 }
1256
1257 return vm_manager.MapCodeMemory(dst_address, src_address, size);
1258}
1259
1260ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1261 u64 src_address, u64 size) {
1262 LOG_DEBUG(Kernel_SVC,
1263 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
1264 "size=0x{:016X}",
1265 process_handle, dst_address, src_address, size);
1266
1267 if (!Common::Is4KBAligned(dst_address)) {
1268 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1269 dst_address);
1270 return ERR_INVALID_ADDRESS;
1271 }
1272
1273 if (!Common::Is4KBAligned(src_address)) {
1274 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1275 src_address);
1276 return ERR_INVALID_ADDRESS;
1277 }
1278
1279 if (size == 0 || Common::Is4KBAligned(size)) {
1280 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1281 return ERR_INVALID_SIZE;
1282 }
1283
1284 if (!IsValidAddressRange(dst_address, size)) {
1285 LOG_ERROR(Kernel_SVC,
1286 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1287 "size=0x{:016X}).",
1288 dst_address, size);
1289 return ERR_INVALID_ADDRESS_STATE;
1290 }
1291
1292 if (!IsValidAddressRange(src_address, size)) {
1293 LOG_ERROR(Kernel_SVC,
1294 "Source address range overflows the address space (src_address=0x{:016X}, "
1295 "size=0x{:016X}).",
1296 src_address, size);
1297 return ERR_INVALID_ADDRESS_STATE;
1298 }
1299
1300 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1301 auto process = handle_table.Get<Process>(process_handle);
1302 if (!process) {
1303 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1304 process_handle);
1305 return ERR_INVALID_HANDLE;
1306 }
1307
1308 auto& vm_manager = process->VMManager();
1309 if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
1310 LOG_ERROR(Kernel_SVC,
1311 "Source address range is not within the address space (src_address=0x{:016X}, "
1312 "size=0x{:016X}).",
1313 src_address, size);
1314 return ERR_INVALID_ADDRESS_STATE;
1315 }
1316
1317 if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
1318 LOG_ERROR(Kernel_SVC,
1319 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1320 "size=0x{:016X}).",
1321 dst_address, size);
1322 return ERR_INVALID_MEMORY_RANGE;
1323 }
1324
1325 return vm_manager.UnmapCodeMemory(dst_address, src_address, size);
1326}
1327
1186/// Exits the current process 1328/// Exits the current process
1187static void ExitProcess() { 1329static void ExitProcess(Core::System& system) {
1188 auto* current_process = Core::CurrentProcess(); 1330 auto* current_process = system.Kernel().CurrentProcess();
1189 1331
1190 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); 1332 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
1191 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, 1333 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
@@ -1194,20 +1336,20 @@ static void ExitProcess() {
1194 current_process->PrepareForTermination(); 1336 current_process->PrepareForTermination();
1195 1337
1196 // Kill the current thread 1338 // Kill the current thread
1197 GetCurrentThread()->Stop(); 1339 system.CurrentScheduler().GetCurrentThread()->Stop();
1198 1340
1199 Core::System::GetInstance().PrepareReschedule(); 1341 system.PrepareReschedule();
1200} 1342}
1201 1343
1202/// Creates a new thread 1344/// Creates a new thread
1203static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, 1345static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
1204 u32 priority, s32 processor_id) { 1346 VAddr stack_top, u32 priority, s32 processor_id) {
1205 LOG_TRACE(Kernel_SVC, 1347 LOG_TRACE(Kernel_SVC,
1206 "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " 1348 "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
1207 "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", 1349 "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
1208 entry_point, arg, stack_top, priority, processor_id, *out_handle); 1350 entry_point, arg, stack_top, priority, processor_id, *out_handle);
1209 1351
1210 auto* const current_process = Core::CurrentProcess(); 1352 auto* const current_process = system.Kernel().CurrentProcess();
1211 1353
1212 if (processor_id == THREADPROCESSORID_IDEAL) { 1354 if (processor_id == THREADPROCESSORID_IDEAL) {
1213 // Set the target CPU to the one specified by the process. 1355 // Set the target CPU to the one specified by the process.
@@ -1239,7 +1381,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
1239 } 1381 }
1240 1382
1241 const std::string name = fmt::format("thread-{:X}", entry_point); 1383 const std::string name = fmt::format("thread-{:X}", entry_point);
1242 auto& kernel = Core::System::GetInstance().Kernel(); 1384 auto& kernel = system.Kernel();
1243 CASCADE_RESULT(SharedPtr<Thread> thread, 1385 CASCADE_RESULT(SharedPtr<Thread> thread,
1244 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, 1386 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
1245 *current_process)); 1387 *current_process));
@@ -1253,16 +1395,16 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
1253 thread->SetGuestHandle(*new_guest_handle); 1395 thread->SetGuestHandle(*new_guest_handle);
1254 *out_handle = *new_guest_handle; 1396 *out_handle = *new_guest_handle;
1255 1397
1256 Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); 1398 system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
1257 1399
1258 return RESULT_SUCCESS; 1400 return RESULT_SUCCESS;
1259} 1401}
1260 1402
1261/// Starts the thread for the provided handle 1403/// Starts the thread for the provided handle
1262static ResultCode StartThread(Handle thread_handle) { 1404static ResultCode StartThread(Core::System& system, Handle thread_handle) {
1263 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 1405 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
1264 1406
1265 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1407 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1266 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); 1408 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1267 if (!thread) { 1409 if (!thread) {
1268 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", 1410 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
@@ -1275,16 +1417,14 @@ static ResultCode StartThread(Handle thread_handle) {
1275 thread->ResumeFromWait(); 1417 thread->ResumeFromWait();
1276 1418
1277 if (thread->GetStatus() == ThreadStatus::Ready) { 1419 if (thread->GetStatus() == ThreadStatus::Ready) {
1278 Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); 1420 system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
1279 } 1421 }
1280 1422
1281 return RESULT_SUCCESS; 1423 return RESULT_SUCCESS;
1282} 1424}
1283 1425
1284/// Called when a thread exits 1426/// Called when a thread exits
1285static void ExitThread() { 1427static void ExitThread(Core::System& system) {
1286 auto& system = Core::System::GetInstance();
1287
1288 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); 1428 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
1289 1429
1290 auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); 1430 auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
@@ -1294,7 +1434,7 @@ static void ExitThread() {
1294} 1434}
1295 1435
1296/// Sleep the current thread 1436/// Sleep the current thread
1297static void SleepThread(s64 nanoseconds) { 1437static void SleepThread(Core::System& system, s64 nanoseconds) {
1298 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); 1438 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
1299 1439
1300 enum class SleepType : s64 { 1440 enum class SleepType : s64 {
@@ -1303,7 +1443,6 @@ static void SleepThread(s64 nanoseconds) {
1303 YieldAndWaitForLoadBalancing = -2, 1443 YieldAndWaitForLoadBalancing = -2,
1304 }; 1444 };
1305 1445
1306 auto& system = Core::System::GetInstance();
1307 auto& scheduler = system.CurrentScheduler(); 1446 auto& scheduler = system.CurrentScheduler();
1308 auto* const current_thread = scheduler.GetCurrentThread(); 1447 auto* const current_thread = scheduler.GetCurrentThread();
1309 1448
@@ -1332,14 +1471,29 @@ static void SleepThread(s64 nanoseconds) {
1332} 1471}
1333 1472
1334/// Wait process wide key atomic 1473/// Wait process wide key atomic
1335static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr, 1474static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr,
1336 Handle thread_handle, s64 nano_seconds) { 1475 VAddr condition_variable_addr, Handle thread_handle,
1476 s64 nano_seconds) {
1337 LOG_TRACE( 1477 LOG_TRACE(
1338 Kernel_SVC, 1478 Kernel_SVC,
1339 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", 1479 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
1340 mutex_addr, condition_variable_addr, thread_handle, nano_seconds); 1480 mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
1341 1481
1342 auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); 1482 if (Memory::IsKernelVirtualAddress(mutex_addr)) {
1483 LOG_ERROR(
1484 Kernel_SVC,
1485 "Given mutex address must not be within the kernel address space. address=0x{:016X}",
1486 mutex_addr);
1487 return ERR_INVALID_ADDRESS_STATE;
1488 }
1489
1490 if (!Common::IsWordAligned(mutex_addr)) {
1491 LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}",
1492 mutex_addr);
1493 return ERR_INVALID_ADDRESS;
1494 }
1495
1496 auto* const current_process = system.Kernel().CurrentProcess();
1343 const auto& handle_table = current_process->GetHandleTable(); 1497 const auto& handle_table = current_process->GetHandleTable();
1344 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); 1498 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1345 ASSERT(thread); 1499 ASSERT(thread);
@@ -1349,7 +1503,7 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
1349 return release_result; 1503 return release_result;
1350 } 1504 }
1351 1505
1352 SharedPtr<Thread> current_thread = GetCurrentThread(); 1506 SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
1353 current_thread->SetCondVarWaitAddress(condition_variable_addr); 1507 current_thread->SetCondVarWaitAddress(condition_variable_addr);
1354 current_thread->SetMutexWaitAddress(mutex_addr); 1508 current_thread->SetMutexWaitAddress(mutex_addr);
1355 current_thread->SetWaitHandle(thread_handle); 1509 current_thread->SetWaitHandle(thread_handle);
@@ -1360,19 +1514,20 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
1360 1514
1361 // Note: Deliberately don't attempt to inherit the lock owner's priority. 1515 // Note: Deliberately don't attempt to inherit the lock owner's priority.
1362 1516
1363 Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); 1517 system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
1364 return RESULT_SUCCESS; 1518 return RESULT_SUCCESS;
1365} 1519}
1366 1520
1367/// Signal process wide key 1521/// Signal process wide key
1368static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { 1522static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr,
1523 s32 target) {
1369 LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", 1524 LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
1370 condition_variable_addr, target); 1525 condition_variable_addr, target);
1371 1526
1372 const auto RetrieveWaitingThreads = [](std::size_t core_index, 1527 const auto RetrieveWaitingThreads = [&system](std::size_t core_index,
1373 std::vector<SharedPtr<Thread>>& waiting_threads, 1528 std::vector<SharedPtr<Thread>>& waiting_threads,
1374 VAddr condvar_addr) { 1529 VAddr condvar_addr) {
1375 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 1530 const auto& scheduler = system.Scheduler(core_index);
1376 const auto& thread_list = scheduler.GetThreadList(); 1531 const auto& thread_list = scheduler.GetThreadList();
1377 1532
1378 for (const auto& thread : thread_list) { 1533 for (const auto& thread : thread_list) {
@@ -1411,9 +1566,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
1411 // liberate Cond Var Thread. 1566 // liberate Cond Var Thread.
1412 thread->SetCondVarWaitAddress(0); 1567 thread->SetCondVarWaitAddress(0);
1413 1568
1414 std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); 1569 const std::size_t current_core = system.CurrentCoreIndex();
1415 1570 auto& monitor = system.Monitor();
1416 auto& monitor = Core::System::GetInstance().Monitor();
1417 1571
1418 // Atomically read the value of the mutex. 1572 // Atomically read the value of the mutex.
1419 u32 mutex_val = 0; 1573 u32 mutex_val = 0;
@@ -1442,7 +1596,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
1442 thread->SetLockOwner(nullptr); 1596 thread->SetLockOwner(nullptr);
1443 thread->SetMutexWaitAddress(0); 1597 thread->SetMutexWaitAddress(0);
1444 thread->SetWaitHandle(0); 1598 thread->SetWaitHandle(0);
1445 Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); 1599 system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
1446 } else { 1600 } else {
1447 // Atomically signal that the mutex now has a waiting thread. 1601 // Atomically signal that the mutex now has a waiting thread.
1448 do { 1602 do {
@@ -1458,7 +1612,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
1458 1612
1459 // The mutex is already owned by some other thread, make this thread wait on it. 1613 // The mutex is already owned by some other thread, make this thread wait on it.
1460 const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); 1614 const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
1461 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1615 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1462 auto owner = handle_table.Get<Thread>(owner_handle); 1616 auto owner = handle_table.Get<Thread>(owner_handle);
1463 ASSERT(owner); 1617 ASSERT(owner);
1464 ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); 1618 ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
@@ -1473,14 +1627,17 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
1473} 1627}
1474 1628
1475// Wait for an address (via Address Arbiter) 1629// Wait for an address (via Address Arbiter)
1476static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) { 1630static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
1631 s64 timeout) {
1477 LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", 1632 LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}",
1478 address, type, value, timeout); 1633 address, type, value, timeout);
1634
1479 // If the passed address is a kernel virtual address, return invalid memory state. 1635 // If the passed address is a kernel virtual address, return invalid memory state.
1480 if (Memory::IsKernelVirtualAddress(address)) { 1636 if (Memory::IsKernelVirtualAddress(address)) {
1481 LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); 1637 LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
1482 return ERR_INVALID_ADDRESS_STATE; 1638 return ERR_INVALID_ADDRESS_STATE;
1483 } 1639 }
1640
1484 // If the address is not properly aligned to 4 bytes, return invalid address. 1641 // If the address is not properly aligned to 4 bytes, return invalid address.
1485 if (!Common::IsWordAligned(address)) { 1642 if (!Common::IsWordAligned(address)) {
1486 LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); 1643 LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
@@ -1488,20 +1645,22 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout
1488 } 1645 }
1489 1646
1490 const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); 1647 const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type);
1491 auto& address_arbiter = 1648 auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
1492 Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter();
1493 return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); 1649 return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
1494} 1650}
1495 1651
1496// Signals to an address (via Address Arbiter) 1652// Signals to an address (via Address Arbiter)
1497static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) { 1653static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value,
1654 s32 num_to_wake) {
1498 LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", 1655 LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
1499 address, type, value, num_to_wake); 1656 address, type, value, num_to_wake);
1657
1500 // If the passed address is a kernel virtual address, return invalid memory state. 1658 // If the passed address is a kernel virtual address, return invalid memory state.
1501 if (Memory::IsKernelVirtualAddress(address)) { 1659 if (Memory::IsKernelVirtualAddress(address)) {
1502 LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); 1660 LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
1503 return ERR_INVALID_ADDRESS_STATE; 1661 return ERR_INVALID_ADDRESS_STATE;
1504 } 1662 }
1663
1505 // If the address is not properly aligned to 4 bytes, return invalid address. 1664 // If the address is not properly aligned to 4 bytes, return invalid address.
1506 if (!Common::IsWordAligned(address)) { 1665 if (!Common::IsWordAligned(address)) {
1507 LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); 1666 LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
@@ -1509,16 +1668,15 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
1509 } 1668 }
1510 1669
1511 const auto signal_type = static_cast<AddressArbiter::SignalType>(type); 1670 const auto signal_type = static_cast<AddressArbiter::SignalType>(type);
1512 auto& address_arbiter = 1671 auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
1513 Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter();
1514 return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); 1672 return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
1515} 1673}
1516 1674
1517/// This returns the total CPU ticks elapsed since the CPU was powered-on 1675/// This returns the total CPU ticks elapsed since the CPU was powered-on
1518static u64 GetSystemTick() { 1676static u64 GetSystemTick(Core::System& system) {
1519 LOG_TRACE(Kernel_SVC, "called"); 1677 LOG_TRACE(Kernel_SVC, "called");
1520 1678
1521 auto& core_timing = Core::System::GetInstance().CoreTiming(); 1679 auto& core_timing = system.CoreTiming();
1522 const u64 result{core_timing.GetTicks()}; 1680 const u64 result{core_timing.GetTicks()};
1523 1681
1524 // Advance time to defeat dumb games that busy-wait for the frame to end. 1682 // Advance time to defeat dumb games that busy-wait for the frame to end.
@@ -1528,18 +1686,18 @@ static u64 GetSystemTick() {
1528} 1686}
1529 1687
1530/// Close a handle 1688/// Close a handle
1531static ResultCode CloseHandle(Handle handle) { 1689static ResultCode CloseHandle(Core::System& system, Handle handle) {
1532 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); 1690 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
1533 1691
1534 auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1692 auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1535 return handle_table.Close(handle); 1693 return handle_table.Close(handle);
1536} 1694}
1537 1695
1538/// Clears the signaled state of an event or process. 1696/// Clears the signaled state of an event or process.
1539static ResultCode ResetSignal(Handle handle) { 1697static ResultCode ResetSignal(Core::System& system, Handle handle) {
1540 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); 1698 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
1541 1699
1542 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1700 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1543 1701
1544 auto event = handle_table.Get<ReadableEvent>(handle); 1702 auto event = handle_table.Get<ReadableEvent>(handle);
1545 if (event) { 1703 if (event) {
@@ -1556,7 +1714,8 @@ static ResultCode ResetSignal(Handle handle) {
1556} 1714}
1557 1715
1558/// Creates a TransferMemory object 1716/// Creates a TransferMemory object
1559static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { 1717static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size,
1718 u32 permissions) {
1560 LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, 1719 LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
1561 permissions); 1720 permissions);
1562 1721
@@ -1584,7 +1743,7 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
1584 return ERR_INVALID_MEMORY_PERMISSIONS; 1743 return ERR_INVALID_MEMORY_PERMISSIONS;
1585 } 1744 }
1586 1745
1587 auto& kernel = Core::System::GetInstance().Kernel(); 1746 auto& kernel = system.Kernel();
1588 auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); 1747 auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms);
1589 1748
1590 auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); 1749 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
@@ -1597,7 +1756,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
1597 return RESULT_SUCCESS; 1756 return RESULT_SUCCESS;
1598} 1757}
1599 1758
1600static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 permission_raw) { 1759static ResultCode MapTransferMemory(Core::System& system, Handle handle, VAddr address, u64 size,
1760 u32 permission_raw) {
1601 LOG_DEBUG(Kernel_SVC, 1761 LOG_DEBUG(Kernel_SVC,
1602 "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}", 1762 "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}",
1603 handle, address, size, permission_raw); 1763 handle, address, size, permission_raw);
@@ -1631,7 +1791,7 @@ static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32
1631 return ERR_INVALID_STATE; 1791 return ERR_INVALID_STATE;
1632 } 1792 }
1633 1793
1634 const auto& kernel = Core::System::GetInstance().Kernel(); 1794 const auto& kernel = system.Kernel();
1635 const auto* const current_process = kernel.CurrentProcess(); 1795 const auto* const current_process = kernel.CurrentProcess();
1636 const auto& handle_table = current_process->GetHandleTable(); 1796 const auto& handle_table = current_process->GetHandleTable();
1637 1797
@@ -1653,7 +1813,8 @@ static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32
1653 return transfer_memory->MapMemory(address, size, permissions); 1813 return transfer_memory->MapMemory(address, size, permissions);
1654} 1814}
1655 1815
1656static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { 1816static ResultCode UnmapTransferMemory(Core::System& system, Handle handle, VAddr address,
1817 u64 size) {
1657 LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle, 1818 LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle,
1658 address, size); 1819 address, size);
1659 1820
@@ -1678,7 +1839,7 @@ static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) {
1678 return ERR_INVALID_ADDRESS_STATE; 1839 return ERR_INVALID_ADDRESS_STATE;
1679 } 1840 }
1680 1841
1681 const auto& kernel = Core::System::GetInstance().Kernel(); 1842 const auto& kernel = system.Kernel();
1682 const auto* const current_process = kernel.CurrentProcess(); 1843 const auto* const current_process = kernel.CurrentProcess();
1683 const auto& handle_table = current_process->GetHandleTable(); 1844 const auto& handle_table = current_process->GetHandleTable();
1684 1845
@@ -1700,10 +1861,11 @@ static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) {
1700 return transfer_memory->UnmapMemory(address, size); 1861 return transfer_memory->UnmapMemory(address, size);
1701} 1862}
1702 1863
1703static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { 1864static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core,
1865 u64* mask) {
1704 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 1866 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
1705 1867
1706 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1868 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1707 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); 1869 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1708 if (!thread) { 1870 if (!thread) {
1709 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", 1871 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
@@ -1717,11 +1879,12 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
1717 return RESULT_SUCCESS; 1879 return RESULT_SUCCESS;
1718} 1880}
1719 1881
1720static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { 1882static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
1883 u64 mask) {
1721 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle, 1884 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle,
1722 mask, core); 1885 mask, core);
1723 1886
1724 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1887 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1725 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); 1888 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1726 if (!thread) { 1889 if (!thread) {
1727 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", 1890 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
@@ -1766,8 +1929,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1766 return RESULT_SUCCESS; 1929 return RESULT_SUCCESS;
1767} 1930}
1768 1931
1769static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions, 1932static ResultCode CreateSharedMemory(Core::System& system, Handle* handle, u64 size,
1770 u32 remote_permissions) { 1933 u32 local_permissions, u32 remote_permissions) {
1771 LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, 1934 LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size,
1772 local_permissions, remote_permissions); 1935 local_permissions, remote_permissions);
1773 if (size == 0) { 1936 if (size == 0) {
@@ -1803,7 +1966,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1803 return ERR_INVALID_MEMORY_PERMISSIONS; 1966 return ERR_INVALID_MEMORY_PERMISSIONS;
1804 } 1967 }
1805 1968
1806 auto& kernel = Core::System::GetInstance().Kernel(); 1969 auto& kernel = system.Kernel();
1807 auto process = kernel.CurrentProcess(); 1970 auto process = kernel.CurrentProcess();
1808 auto& handle_table = process->GetHandleTable(); 1971 auto& handle_table = process->GetHandleTable();
1809 auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms); 1972 auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms);
@@ -1812,10 +1975,10 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1812 return RESULT_SUCCESS; 1975 return RESULT_SUCCESS;
1813} 1976}
1814 1977
1815static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) { 1978static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) {
1816 LOG_DEBUG(Kernel_SVC, "called"); 1979 LOG_DEBUG(Kernel_SVC, "called");
1817 1980
1818 auto& kernel = Core::System::GetInstance().Kernel(); 1981 auto& kernel = system.Kernel();
1819 const auto [readable_event, writable_event] = 1982 const auto [readable_event, writable_event] =
1820 WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent"); 1983 WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent");
1821 1984
@@ -1840,10 +2003,10 @@ static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) {
1840 return RESULT_SUCCESS; 2003 return RESULT_SUCCESS;
1841} 2004}
1842 2005
1843static ResultCode ClearEvent(Handle handle) { 2006static ResultCode ClearEvent(Core::System& system, Handle handle) {
1844 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); 2007 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
1845 2008
1846 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 2009 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1847 2010
1848 auto writable_event = handle_table.Get<WritableEvent>(handle); 2011 auto writable_event = handle_table.Get<WritableEvent>(handle);
1849 if (writable_event) { 2012 if (writable_event) {
@@ -1861,10 +2024,10 @@ static ResultCode ClearEvent(Handle handle) {
1861 return ERR_INVALID_HANDLE; 2024 return ERR_INVALID_HANDLE;
1862} 2025}
1863 2026
1864static ResultCode SignalEvent(Handle handle) { 2027static ResultCode SignalEvent(Core::System& system, Handle handle) {
1865 LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); 2028 LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle);
1866 2029
1867 HandleTable& handle_table = Core::CurrentProcess()->GetHandleTable(); 2030 HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1868 auto writable_event = handle_table.Get<WritableEvent>(handle); 2031 auto writable_event = handle_table.Get<WritableEvent>(handle);
1869 2032
1870 if (!writable_event) { 2033 if (!writable_event) {
@@ -1876,7 +2039,7 @@ static ResultCode SignalEvent(Handle handle) {
1876 return RESULT_SUCCESS; 2039 return RESULT_SUCCESS;
1877} 2040}
1878 2041
1879static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { 2042static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
1880 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); 2043 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
1881 2044
1882 // This function currently only allows retrieving a process' status. 2045 // This function currently only allows retrieving a process' status.
@@ -1884,7 +2047,7 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1884 Status, 2047 Status,
1885 }; 2048 };
1886 2049
1887 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 2050 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1888 const auto process = handle_table.Get<Process>(process_handle); 2051 const auto process = handle_table.Get<Process>(process_handle);
1889 if (!process) { 2052 if (!process) {
1890 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", 2053 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
@@ -1902,10 +2065,10 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1902 return RESULT_SUCCESS; 2065 return RESULT_SUCCESS;
1903} 2066}
1904 2067
1905static ResultCode CreateResourceLimit(Handle* out_handle) { 2068static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) {
1906 LOG_DEBUG(Kernel_SVC, "called"); 2069 LOG_DEBUG(Kernel_SVC, "called");
1907 2070
1908 auto& kernel = Core::System::GetInstance().Kernel(); 2071 auto& kernel = system.Kernel();
1909 auto resource_limit = ResourceLimit::Create(kernel); 2072 auto resource_limit = ResourceLimit::Create(kernel);
1910 2073
1911 auto* const current_process = kernel.CurrentProcess(); 2074 auto* const current_process = kernel.CurrentProcess();
@@ -1920,11 +2083,11 @@ static ResultCode CreateResourceLimit(Handle* out_handle) {
1920 return RESULT_SUCCESS; 2083 return RESULT_SUCCESS;
1921} 2084}
1922 2085
1923static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_limit, 2086static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_value,
1924 u32 resource_type) { 2087 Handle resource_limit, u32 resource_type) {
1925 LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); 2088 LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
1926 2089
1927 const auto limit_value = RetrieveResourceLimitValue(resource_limit, resource_type, 2090 const auto limit_value = RetrieveResourceLimitValue(system, resource_limit, resource_type,
1928 ResourceLimitValueType::LimitValue); 2091 ResourceLimitValueType::LimitValue);
1929 if (limit_value.Failed()) { 2092 if (limit_value.Failed()) {
1930 return limit_value.Code(); 2093 return limit_value.Code();
@@ -1934,11 +2097,11 @@ static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_lim
1934 return RESULT_SUCCESS; 2097 return RESULT_SUCCESS;
1935} 2098}
1936 2099
1937static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_limit, 2100static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_value,
1938 u32 resource_type) { 2101 Handle resource_limit, u32 resource_type) {
1939 LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); 2102 LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
1940 2103
1941 const auto current_value = RetrieveResourceLimitValue(resource_limit, resource_type, 2104 const auto current_value = RetrieveResourceLimitValue(system, resource_limit, resource_type,
1942 ResourceLimitValueType::CurrentValue); 2105 ResourceLimitValueType::CurrentValue);
1943 if (current_value.Failed()) { 2106 if (current_value.Failed()) {
1944 return current_value.Code(); 2107 return current_value.Code();
@@ -1948,7 +2111,8 @@ static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_l
1948 return RESULT_SUCCESS; 2111 return RESULT_SUCCESS;
1949} 2112}
1950 2113
1951static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource_type, u64 value) { 2114static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit,
2115 u32 resource_type, u64 value) {
1952 LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit, 2116 LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit,
1953 resource_type, value); 2117 resource_type, value);
1954 2118
@@ -1958,8 +2122,7 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource
1958 return ERR_INVALID_ENUM_VALUE; 2122 return ERR_INVALID_ENUM_VALUE;
1959 } 2123 }
1960 2124
1961 auto& kernel = Core::System::GetInstance().Kernel(); 2125 auto* const current_process = system.Kernel().CurrentProcess();
1962 auto* const current_process = kernel.CurrentProcess();
1963 ASSERT(current_process != nullptr); 2126 ASSERT(current_process != nullptr);
1964 2127
1965 auto resource_limit_object = 2128 auto resource_limit_object =
@@ -1983,8 +2146,8 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource
1983 return RESULT_SUCCESS; 2146 return RESULT_SUCCESS;
1984} 2147}
1985 2148
1986static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, 2149static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
1987 u32 out_process_ids_size) { 2150 VAddr out_process_ids, u32 out_process_ids_size) {
1988 LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", 2151 LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
1989 out_process_ids, out_process_ids_size); 2152 out_process_ids, out_process_ids_size);
1990 2153
@@ -1996,7 +2159,7 @@ static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids,
1996 return ERR_OUT_OF_RANGE; 2159 return ERR_OUT_OF_RANGE;
1997 } 2160 }
1998 2161
1999 const auto& kernel = Core::System::GetInstance().Kernel(); 2162 const auto& kernel = system.Kernel();
2000 const auto& vm_manager = kernel.CurrentProcess()->VMManager(); 2163 const auto& vm_manager = kernel.CurrentProcess()->VMManager();
2001 const auto total_copy_size = out_process_ids_size * sizeof(u64); 2164 const auto total_copy_size = out_process_ids_size * sizeof(u64);
2002 2165
@@ -2020,8 +2183,8 @@ static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids,
2020 return RESULT_SUCCESS; 2183 return RESULT_SUCCESS;
2021} 2184}
2022 2185
2023ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size, 2186ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
2024 Handle debug_handle) { 2187 u32 out_thread_ids_size, Handle debug_handle) {
2025 // TODO: Handle this case when debug events are supported. 2188 // TODO: Handle this case when debug events are supported.
2026 UNIMPLEMENTED_IF(debug_handle != InvalidHandle); 2189 UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
2027 2190
@@ -2035,7 +2198,7 @@ ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thr
2035 return ERR_OUT_OF_RANGE; 2198 return ERR_OUT_OF_RANGE;
2036 } 2199 }
2037 2200
2038 const auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); 2201 const auto* const current_process = system.Kernel().CurrentProcess();
2039 const auto& vm_manager = current_process->VMManager(); 2202 const auto& vm_manager = current_process->VMManager();
2040 const auto total_copy_size = out_thread_ids_size * sizeof(u64); 2203 const auto total_copy_size = out_thread_ids_size * sizeof(u64);
2041 2204
@@ -2062,7 +2225,7 @@ ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thr
2062 2225
2063namespace { 2226namespace {
2064struct FunctionDef { 2227struct FunctionDef {
2065 using Func = void(); 2228 using Func = void(Core::System&);
2066 2229
2067 u32 id; 2230 u32 id;
2068 Func* func; 2231 Func* func;
@@ -2190,8 +2353,8 @@ static const FunctionDef SVC_Table[] = {
2190 {0x74, nullptr, "MapProcessMemory"}, 2353 {0x74, nullptr, "MapProcessMemory"},
2191 {0x75, nullptr, "UnmapProcessMemory"}, 2354 {0x75, nullptr, "UnmapProcessMemory"},
2192 {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, 2355 {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},
2193 {0x77, nullptr, "MapProcessCodeMemory"}, 2356 {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"},
2194 {0x78, nullptr, "UnmapProcessCodeMemory"}, 2357 {0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
2195 {0x79, nullptr, "CreateProcess"}, 2358 {0x79, nullptr, "CreateProcess"},
2196 {0x7A, nullptr, "StartProcess"}, 2359 {0x7A, nullptr, "StartProcess"},
2197 {0x7B, nullptr, "TerminateProcess"}, 2360 {0x7B, nullptr, "TerminateProcess"},
@@ -2211,7 +2374,7 @@ static const FunctionDef* GetSVCInfo(u32 func_num) {
2211 2374
2212MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); 2375MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
2213 2376
2214void CallSVC(u32 immediate) { 2377void CallSVC(Core::System& system, u32 immediate) {
2215 MICROPROFILE_SCOPE(Kernel_SVC); 2378 MICROPROFILE_SCOPE(Kernel_SVC);
2216 2379
2217 // Lock the global kernel mutex when we enter the kernel HLE. 2380 // Lock the global kernel mutex when we enter the kernel HLE.
@@ -2220,7 +2383,7 @@ void CallSVC(u32 immediate) {
2220 const FunctionDef* info = GetSVCInfo(immediate); 2383 const FunctionDef* info = GetSVCInfo(immediate);
2221 if (info) { 2384 if (info) {
2222 if (info->func) { 2385 if (info->func) {
2223 info->func(); 2386 info->func(system);
2224 } else { 2387 } else {
2225 LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name); 2388 LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name);
2226 } 2389 }
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index c37ae0f98..c5539ac1c 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -6,8 +6,12 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace Kernel { 13namespace Kernel {
10 14
11void CallSVC(u32 immediate); 15void CallSVC(Core::System& system, u32 immediate);
12 16
13} // namespace Kernel 17} // namespace Kernel
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index b3733680f..865473c6f 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -11,278 +11,319 @@
11 11
12namespace Kernel { 12namespace Kernel {
13 13
14static inline u64 Param(int n) { 14static inline u64 Param(const Core::System& system, int n) {
15 return Core::CurrentArmInterface().GetReg(n); 15 return system.CurrentArmInterface().GetReg(n);
16} 16}
17 17
18/** 18/**
19 * HLE a function return from the current ARM userland process 19 * HLE a function return from the current ARM userland process
20 * @param res Result to return 20 * @param system System context
21 * @param result Result to return
21 */ 22 */
22static inline void FuncReturn(u64 res) { 23static inline void FuncReturn(Core::System& system, u64 result) {
23 Core::CurrentArmInterface().SetReg(0, res); 24 system.CurrentArmInterface().SetReg(0, result);
24} 25}
25 26
26//////////////////////////////////////////////////////////////////////////////////////////////////// 27////////////////////////////////////////////////////////////////////////////////////////////////////
27// Function wrappers that return type ResultCode 28// Function wrappers that return type ResultCode
28 29
29template <ResultCode func(u64)> 30template <ResultCode func(Core::System&, u64)>
30void SvcWrap() { 31void SvcWrap(Core::System& system) {
31 FuncReturn(func(Param(0)).raw); 32 FuncReturn(system, func(system, Param(system, 0)).raw);
32} 33}
33 34
34template <ResultCode func(u32)> 35template <ResultCode func(Core::System&, u32)>
35void SvcWrap() { 36void SvcWrap(Core::System& system) {
36 FuncReturn(func(static_cast<u32>(Param(0))).raw); 37 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
37} 38}
38 39
39template <ResultCode func(u32, u32)> 40template <ResultCode func(Core::System&, u32, u32)>
40void SvcWrap() { 41void SvcWrap(Core::System& system) {
41 FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw); 42 FuncReturn(
43 system,
44 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
45}
46
47template <ResultCode func(Core::System&, u32, u64, u64, u64)>
48void SvcWrap(Core::System& system) {
49 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
50 Param(system, 2), Param(system, 3))
51 .raw);
42} 52}
43 53
44template <ResultCode func(u32*)> 54template <ResultCode func(Core::System&, u32*)>
45void SvcWrap() { 55void SvcWrap(Core::System& system) {
46 u32 param = 0; 56 u32 param = 0;
47 const u32 retval = func(&param).raw; 57 const u32 retval = func(system, &param).raw;
48 Core::CurrentArmInterface().SetReg(1, param); 58 system.CurrentArmInterface().SetReg(1, param);
49 FuncReturn(retval); 59 FuncReturn(system, retval);
50} 60}
51 61
52template <ResultCode func(u32*, u32)> 62template <ResultCode func(Core::System&, u32*, u32)>
53void SvcWrap() { 63void SvcWrap(Core::System& system) {
54 u32 param_1 = 0; 64 u32 param_1 = 0;
55 u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw; 65 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
56 Core::CurrentArmInterface().SetReg(1, param_1); 66 system.CurrentArmInterface().SetReg(1, param_1);
57 FuncReturn(retval); 67 FuncReturn(system, retval);
58} 68}
59 69
60template <ResultCode func(u32*, u32*)> 70template <ResultCode func(Core::System&, u32*, u32*)>
61void SvcWrap() { 71void SvcWrap(Core::System& system) {
62 u32 param_1 = 0; 72 u32 param_1 = 0;
63 u32 param_2 = 0; 73 u32 param_2 = 0;
64 const u32 retval = func(&param_1, &param_2).raw; 74 const u32 retval = func(system, &param_1, &param_2).raw;
65 75
66 auto& arm_interface = Core::CurrentArmInterface(); 76 auto& arm_interface = system.CurrentArmInterface();
67 arm_interface.SetReg(1, param_1); 77 arm_interface.SetReg(1, param_1);
68 arm_interface.SetReg(2, param_2); 78 arm_interface.SetReg(2, param_2);
69 79
70 FuncReturn(retval); 80 FuncReturn(system, retval);
71} 81}
72 82
73template <ResultCode func(u32*, u64)> 83template <ResultCode func(Core::System&, u32*, u64)>
74void SvcWrap() { 84void SvcWrap(Core::System& system) {
75 u32 param_1 = 0; 85 u32 param_1 = 0;
76 const u32 retval = func(&param_1, Param(1)).raw; 86 const u32 retval = func(system, &param_1, Param(system, 1)).raw;
77 Core::CurrentArmInterface().SetReg(1, param_1); 87 system.CurrentArmInterface().SetReg(1, param_1);
78 FuncReturn(retval); 88 FuncReturn(system, retval);
79} 89}
80 90
81template <ResultCode func(u32*, u64, u32)> 91template <ResultCode func(Core::System&, u32*, u64, u32)>
82void SvcWrap() { 92void SvcWrap(Core::System& system) {
83 u32 param_1 = 0; 93 u32 param_1 = 0;
84 const u32 retval = func(&param_1, Param(1), static_cast<u32>(Param(2))).raw; 94 const u32 retval =
85 Core::CurrentArmInterface().SetReg(1, param_1); 95 func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw;
86 FuncReturn(retval); 96
97 system.CurrentArmInterface().SetReg(1, param_1);
98 FuncReturn(system, retval);
87} 99}
88 100
89template <ResultCode func(u64*, u32)> 101template <ResultCode func(Core::System&, u64*, u32)>
90void SvcWrap() { 102void SvcWrap(Core::System& system) {
91 u64 param_1 = 0; 103 u64 param_1 = 0;
92 const u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw; 104 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
93 Core::CurrentArmInterface().SetReg(1, param_1); 105
94 FuncReturn(retval); 106 system.CurrentArmInterface().SetReg(1, param_1);
107 FuncReturn(system, retval);
95} 108}
96 109
97template <ResultCode func(u64, s32)> 110template <ResultCode func(Core::System&, u64, s32)>
98void SvcWrap() { 111void SvcWrap(Core::System& system) {
99 FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw); 112 FuncReturn(system, func(system, Param(system, 0), static_cast<s32>(Param(system, 1))).raw);
100} 113}
101 114
102template <ResultCode func(u64, u32)> 115template <ResultCode func(Core::System&, u64, u32)>
103void SvcWrap() { 116void SvcWrap(Core::System& system) {
104 FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw); 117 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
105} 118}
106 119
107template <ResultCode func(u64*, u64)> 120template <ResultCode func(Core::System&, u64*, u64)>
108void SvcWrap() { 121void SvcWrap(Core::System& system) {
109 u64 param_1 = 0; 122 u64 param_1 = 0;
110 u32 retval = func(&param_1, Param(1)).raw; 123 const u32 retval = func(system, &param_1, Param(system, 1)).raw;
111 Core::CurrentArmInterface().SetReg(1, param_1); 124
112 FuncReturn(retval); 125 system.CurrentArmInterface().SetReg(1, param_1);
126 FuncReturn(system, retval);
113} 127}
114 128
115template <ResultCode func(u64*, u32, u32)> 129template <ResultCode func(Core::System&, u64*, u32, u32)>
116void SvcWrap() { 130void SvcWrap(Core::System& system) {
117 u64 param_1 = 0; 131 u64 param_1 = 0;
118 u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw; 132 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)),
119 Core::CurrentArmInterface().SetReg(1, param_1); 133 static_cast<u32>(Param(system, 2)))
120 FuncReturn(retval); 134 .raw;
135
136 system.CurrentArmInterface().SetReg(1, param_1);
137 FuncReturn(system, retval);
121} 138}
122 139
123template <ResultCode func(u32, u64)> 140template <ResultCode func(Core::System&, u32, u64)>
124void SvcWrap() { 141void SvcWrap(Core::System& system) {
125 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw); 142 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
126} 143}
127 144
128template <ResultCode func(u32, u32, u64)> 145template <ResultCode func(Core::System&, u32, u32, u64)>
129void SvcWrap() { 146void SvcWrap(Core::System& system) {
130 FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw); 147 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
148 static_cast<u32>(Param(system, 1)), Param(system, 2))
149 .raw);
131} 150}
132 151
133template <ResultCode func(u32, u32*, u64*)> 152template <ResultCode func(Core::System&, u32, u32*, u64*)>
134void SvcWrap() { 153void SvcWrap(Core::System& system) {
135 u32 param_1 = 0; 154 u32 param_1 = 0;
136 u64 param_2 = 0; 155 u64 param_2 = 0;
137 ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2); 156 const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
138 Core::CurrentArmInterface().SetReg(1, param_1);
139 Core::CurrentArmInterface().SetReg(2, param_2);
140 FuncReturn(retval.raw);
141}
142 157
143template <ResultCode func(u64, u64, u32, u32)> 158 system.CurrentArmInterface().SetReg(1, param_1);
144void SvcWrap() { 159 system.CurrentArmInterface().SetReg(2, param_2);
145 FuncReturn( 160 FuncReturn(system, retval.raw);
146 func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw);
147} 161}
148 162
149template <ResultCode func(u64, u64, u32, u64)> 163template <ResultCode func(Core::System&, u64, u64, u32, u32)>
150void SvcWrap() { 164void SvcWrap(Core::System& system) {
151 FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw); 165 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
166 static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
167 .raw);
152} 168}
153 169
154template <ResultCode func(u32, u64, u32)> 170template <ResultCode func(Core::System&, u64, u64, u32, u64)>
155void SvcWrap() { 171void SvcWrap(Core::System& system) {
156 FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw); 172 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
173 static_cast<u32>(Param(system, 2)), Param(system, 3))
174 .raw);
157} 175}
158 176
159template <ResultCode func(u64, u64, u64)> 177template <ResultCode func(Core::System&, u32, u64, u32)>
160void SvcWrap() { 178void SvcWrap(Core::System& system) {
161 FuncReturn(func(Param(0), Param(1), Param(2)).raw); 179 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
180 static_cast<u32>(Param(system, 2)))
181 .raw);
162} 182}
163 183
164template <ResultCode func(u64, u64, u32)> 184template <ResultCode func(Core::System&, u64, u64, u64)>
165void SvcWrap() { 185void SvcWrap(Core::System& system) {
166 FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw); 186 FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
167} 187}
168 188
169template <ResultCode func(u32, u64, u64, u32)> 189template <ResultCode func(Core::System&, u64, u64, u32)>
170void SvcWrap() { 190void SvcWrap(Core::System& system) {
171 FuncReturn( 191 FuncReturn(
172 func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw); 192 system,
193 func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
173} 194}
174 195
175template <ResultCode func(u32, u64, u64)> 196template <ResultCode func(Core::System&, u32, u64, u64, u32)>
176void SvcWrap() { 197void SvcWrap(Core::System& system) {
177 FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw); 198 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
199 Param(system, 2), static_cast<u32>(Param(system, 3)))
200 .raw);
178} 201}
179 202
180template <ResultCode func(u32*, u64, u64, s64)> 203template <ResultCode func(Core::System&, u32, u64, u64)>
181void SvcWrap() { 204void SvcWrap(Core::System& system) {
205 FuncReturn(
206 system,
207 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
208}
209
210template <ResultCode func(Core::System&, u32*, u64, u64, s64)>
211void SvcWrap(Core::System& system) {
182 u32 param_1 = 0; 212 u32 param_1 = 0;
183 ResultCode retval = 213 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
184 func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))); 214 static_cast<s64>(Param(system, 3)))
185 Core::CurrentArmInterface().SetReg(1, param_1); 215 .raw;
186 FuncReturn(retval.raw); 216
217 system.CurrentArmInterface().SetReg(1, param_1);
218 FuncReturn(system, retval);
187} 219}
188 220
189template <ResultCode func(u64, u64, u32, s64)> 221template <ResultCode func(Core::System&, u64, u64, u32, s64)>
190void SvcWrap() { 222void SvcWrap(Core::System& system) {
191 FuncReturn( 223 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
192 func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw); 224 static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
225 .raw);
193} 226}
194 227
195template <ResultCode func(u64*, u64, u64, u64)> 228template <ResultCode func(Core::System&, u64*, u64, u64, u64)>
196void SvcWrap() { 229void SvcWrap(Core::System& system) {
197 u64 param_1 = 0; 230 u64 param_1 = 0;
198 u32 retval = func(&param_1, Param(1), Param(2), Param(3)).raw; 231 const u32 retval =
199 Core::CurrentArmInterface().SetReg(1, param_1); 232 func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw;
200 FuncReturn(retval); 233
234 system.CurrentArmInterface().SetReg(1, param_1);
235 FuncReturn(system, retval);
201} 236}
202 237
203template <ResultCode func(u32*, u64, u64, u64, u32, s32)> 238template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
204void SvcWrap() { 239void SvcWrap(Core::System& system) {
205 u32 param_1 = 0; 240 u32 param_1 = 0;
206 u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)), 241 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3),
207 static_cast<s32>(Param(5))) 242 static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5)))
208 .raw; 243 .raw;
209 Core::CurrentArmInterface().SetReg(1, param_1); 244
210 FuncReturn(retval); 245 system.CurrentArmInterface().SetReg(1, param_1);
246 FuncReturn(system, retval);
211} 247}
212 248
213template <ResultCode func(u32*, u64, u64, u32)> 249template <ResultCode func(Core::System&, u32*, u64, u64, u32)>
214void SvcWrap() { 250void SvcWrap(Core::System& system) {
215 u32 param_1 = 0; 251 u32 param_1 = 0;
216 u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw; 252 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
217 Core::CurrentArmInterface().SetReg(1, param_1); 253 static_cast<u32>(Param(system, 3)))
218 FuncReturn(retval); 254 .raw;
255
256 system.CurrentArmInterface().SetReg(1, param_1);
257 FuncReturn(system, retval);
219} 258}
220 259
221template <ResultCode func(Handle*, u64, u32, u32)> 260template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
222void SvcWrap() { 261void SvcWrap(Core::System& system) {
223 u32 param_1 = 0; 262 u32 param_1 = 0;
224 u32 retval = 263 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
225 func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw; 264 static_cast<u32>(Param(system, 3)))
226 Core::CurrentArmInterface().SetReg(1, param_1); 265 .raw;
227 FuncReturn(retval); 266
267 system.CurrentArmInterface().SetReg(1, param_1);
268 FuncReturn(system, retval);
228} 269}
229 270
230template <ResultCode func(u64, u32, s32, s64)> 271template <ResultCode func(Core::System&, u64, u32, s32, s64)>
231void SvcWrap() { 272void SvcWrap(Core::System& system) {
232 FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)), 273 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
233 static_cast<s64>(Param(3))) 274 static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
234 .raw); 275 .raw);
235} 276}
236 277
237template <ResultCode func(u64, u32, s32, s32)> 278template <ResultCode func(Core::System&, u64, u32, s32, s32)>
238void SvcWrap() { 279void SvcWrap(Core::System& system) {
239 FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)), 280 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
240 static_cast<s32>(Param(3))) 281 static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
241 .raw); 282 .raw);
242} 283}
243 284
244//////////////////////////////////////////////////////////////////////////////////////////////////// 285////////////////////////////////////////////////////////////////////////////////////////////////////
245// Function wrappers that return type u32 286// Function wrappers that return type u32
246 287
247template <u32 func()> 288template <u32 func(Core::System&)>
248void SvcWrap() { 289void SvcWrap(Core::System& system) {
249 FuncReturn(func()); 290 FuncReturn(system, func(system));
250} 291}
251 292
252//////////////////////////////////////////////////////////////////////////////////////////////////// 293////////////////////////////////////////////////////////////////////////////////////////////////////
253// Function wrappers that return type u64 294// Function wrappers that return type u64
254 295
255template <u64 func()> 296template <u64 func(Core::System&)>
256void SvcWrap() { 297void SvcWrap(Core::System& system) {
257 FuncReturn(func()); 298 FuncReturn(system, func(system));
258} 299}
259 300
260//////////////////////////////////////////////////////////////////////////////////////////////////// 301////////////////////////////////////////////////////////////////////////////////////////////////////
261/// Function wrappers that return type void 302/// Function wrappers that return type void
262 303
263template <void func()> 304template <void func(Core::System&)>
264void SvcWrap() { 305void SvcWrap(Core::System& system) {
265 func(); 306 func(system);
266} 307}
267 308
268template <void func(s64)> 309template <void func(Core::System&, s64)>
269void SvcWrap() { 310void SvcWrap(Core::System& system) {
270 func(static_cast<s64>(Param(0))); 311 func(system, static_cast<s64>(Param(system, 0)));
271} 312}
272 313
273template <void func(u64, u64 len)> 314template <void func(Core::System&, u64, u64)>
274void SvcWrap() { 315void SvcWrap(Core::System& system) {
275 func(Param(0), Param(1)); 316 func(system, Param(system, 0), Param(system, 1));
276} 317}
277 318
278template <void func(u64, u64, u64)> 319template <void func(Core::System&, u64, u64, u64)>
279void SvcWrap() { 320void SvcWrap(Core::System& system) {
280 func(Param(0), Param(1), Param(2)); 321 func(system, Param(system, 0), Param(system, 1), Param(system, 2));
281} 322}
282 323
283template <void func(u32, u64, u64)> 324template <void func(Core::System&, u32, u64, u64)>
284void SvcWrap() { 325void SvcWrap(Core::System& system) {
285 func(static_cast<u32>(Param(0)), Param(1), Param(2)); 326 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
286} 327}
287 328
288} // namespace Kernel 329} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 1b891f632..ca52267b2 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -220,11 +220,6 @@ void Thread::SetPriority(u32 priority) {
220 UpdatePriority(); 220 UpdatePriority();
221} 221}
222 222
223void Thread::BoostPriority(u32 priority) {
224 scheduler->SetThreadPriority(this, priority);
225 current_priority = priority;
226}
227
228void Thread::SetWaitSynchronizationResult(ResultCode result) { 223void Thread::SetWaitSynchronizationResult(ResultCode result) {
229 context.cpu_registers[0] = result.raw; 224 context.cpu_registers[0] = result.raw;
230} 225}
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 73e5d1bb4..32026d7f0 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -106,7 +106,7 @@ public:
106 return "Thread"; 106 return "Thread";
107 } 107 }
108 108
109 static const HandleType HANDLE_TYPE = HandleType::Thread; 109 static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
110 HandleType GetHandleType() const override { 110 HandleType GetHandleType() const override {
111 return HANDLE_TYPE; 111 return HANDLE_TYPE;
112 } 112 }
@@ -136,12 +136,6 @@ public:
136 */ 136 */
137 void SetPriority(u32 priority); 137 void SetPriority(u32 priority);
138 138
139 /**
140 * Temporarily boosts the thread's priority until the next time it is scheduled
141 * @param priority The new priority
142 */
143 void BoostPriority(u32 priority);
144
145 /// Adds a thread to the list of threads that are waiting for a lock held by this thread. 139 /// Adds a thread to the list of threads that are waiting for a lock held by this thread.
146 void AddMutexWaiter(SharedPtr<Thread> thread); 140 void AddMutexWaiter(SharedPtr<Thread> thread);
147 141
diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp
index 23228e1b5..26c4e5e67 100644
--- a/src/core/hle/kernel/transfer_memory.cpp
+++ b/src/core/hle/kernel/transfer_memory.cpp
@@ -14,8 +14,8 @@ namespace Kernel {
14TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {} 14TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {}
15TransferMemory::~TransferMemory() = default; 15TransferMemory::~TransferMemory() = default;
16 16
17SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, 17SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, u64 size,
18 size_t size, MemoryPermission permissions) { 18 MemoryPermission permissions) {
19 SharedPtr<TransferMemory> transfer_memory{new TransferMemory(kernel)}; 19 SharedPtr<TransferMemory> transfer_memory{new TransferMemory(kernel)};
20 20
21 transfer_memory->base_address = base_address; 21 transfer_memory->base_address = base_address;
@@ -26,7 +26,15 @@ SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_
26 return transfer_memory; 26 return transfer_memory;
27} 27}
28 28
29ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermission permissions) { 29const u8* TransferMemory::GetPointer() const {
30 return backing_block.get()->data();
31}
32
33u64 TransferMemory::GetSize() const {
34 return memory_size;
35}
36
37ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission permissions) {
30 if (memory_size != size) { 38 if (memory_size != size) {
31 return ERR_INVALID_SIZE; 39 return ERR_INVALID_SIZE;
32 } 40 }
@@ -39,13 +47,13 @@ ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermissio
39 return ERR_INVALID_STATE; 47 return ERR_INVALID_STATE;
40 } 48 }
41 49
50 backing_block = std::make_shared<std::vector<u8>>(size);
51
42 const auto map_state = owner_permissions == MemoryPermission::None 52 const auto map_state = owner_permissions == MemoryPermission::None
43 ? MemoryState::TransferMemoryIsolated 53 ? MemoryState::TransferMemoryIsolated
44 : MemoryState::TransferMemory; 54 : MemoryState::TransferMemory;
45 auto& vm_manager = owner_process->VMManager(); 55 auto& vm_manager = owner_process->VMManager();
46 const auto map_result = vm_manager.MapMemoryBlock( 56 const auto map_result = vm_manager.MapMemoryBlock(address, backing_block, 0, size, map_state);
47 address, std::make_shared<std::vector<u8>>(size), 0, size, map_state);
48
49 if (map_result.Failed()) { 57 if (map_result.Failed()) {
50 return map_result.Code(); 58 return map_result.Code();
51 } 59 }
@@ -54,7 +62,7 @@ ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermissio
54 return RESULT_SUCCESS; 62 return RESULT_SUCCESS;
55} 63}
56 64
57ResultCode TransferMemory::UnmapMemory(VAddr address, size_t size) { 65ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {
58 if (memory_size != size) { 66 if (memory_size != size) {
59 return ERR_INVALID_SIZE; 67 return ERR_INVALID_SIZE;
60 } 68 }
diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h
index ec294951e..a140b1e2b 100644
--- a/src/core/hle/kernel/transfer_memory.h
+++ b/src/core/hle/kernel/transfer_memory.h
@@ -4,6 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8#include <vector>
9
7#include "core/hle/kernel/object.h" 10#include "core/hle/kernel/object.h"
8 11
9union ResultCode; 12union ResultCode;
@@ -25,7 +28,7 @@ class TransferMemory final : public Object {
25public: 28public:
26 static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; 29 static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
27 30
28 static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, size_t size, 31 static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size,
29 MemoryPermission permissions); 32 MemoryPermission permissions);
30 33
31 TransferMemory(const TransferMemory&) = delete; 34 TransferMemory(const TransferMemory&) = delete;
@@ -46,6 +49,12 @@ public:
46 return HANDLE_TYPE; 49 return HANDLE_TYPE;
47 } 50 }
48 51
52 /// Gets a pointer to the backing block of this instance.
53 const u8* GetPointer() const;
54
55 /// Gets the size of the memory backing this instance in bytes.
56 u64 GetSize() const;
57
49 /// Attempts to map transfer memory with the given range and memory permissions. 58 /// Attempts to map transfer memory with the given range and memory permissions.
50 /// 59 ///
51 /// @param address The base address to being mapping memory at. 60 /// @param address The base address to being mapping memory at.
@@ -56,7 +65,7 @@ public:
56 /// the same values that were given when creating the transfer memory 65 /// the same values that were given when creating the transfer memory
57 /// instance. 66 /// instance.
58 /// 67 ///
59 ResultCode MapMemory(VAddr address, size_t size, MemoryPermission permissions); 68 ResultCode MapMemory(VAddr address, u64 size, MemoryPermission permissions);
60 69
61 /// Unmaps the transfer memory with the given range 70 /// Unmaps the transfer memory with the given range
62 /// 71 ///
@@ -66,17 +75,20 @@ public:
66 /// @pre The given address and size must be the same as the ones used 75 /// @pre The given address and size must be the same as the ones used
67 /// to create the transfer memory instance. 76 /// to create the transfer memory instance.
68 /// 77 ///
69 ResultCode UnmapMemory(VAddr address, size_t size); 78 ResultCode UnmapMemory(VAddr address, u64 size);
70 79
71private: 80private:
72 explicit TransferMemory(KernelCore& kernel); 81 explicit TransferMemory(KernelCore& kernel);
73 ~TransferMemory() override; 82 ~TransferMemory() override;
74 83
84 /// Memory block backing this instance.
85 std::shared_ptr<std::vector<u8>> backing_block;
86
75 /// The base address for the memory managed by this instance. 87 /// The base address for the memory managed by this instance.
76 VAddr base_address = 0; 88 VAddr base_address = 0;
77 89
78 /// Size of the memory, in bytes, that this instance manages. 90 /// Size of the memory, in bytes, that this instance manages.
79 size_t memory_size = 0; 91 u64 memory_size = 0;
80 92
81 /// The memory permissions that are applied to this instance. 93 /// The memory permissions that are applied to this instance.
82 MemoryPermission owner_permissions{}; 94 MemoryPermission owner_permissions{};
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index ec0a480ce..f0c0c12fc 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -302,6 +302,86 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
302 return MakeResult<VAddr>(heap_region_base); 302 return MakeResult<VAddr>(heap_region_base);
303} 303}
304 304
305ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
306 constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
307 const auto src_check_result = CheckRangeState(
308 src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All,
309 VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
310
311 if (src_check_result.Failed()) {
312 return src_check_result.Code();
313 }
314
315 const auto mirror_result =
316 MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode);
317 if (mirror_result.IsError()) {
318 return mirror_result;
319 }
320
321 // Ensure we lock the source memory region.
322 const auto src_vma_result = CarveVMARange(src_address, size);
323 if (src_vma_result.Failed()) {
324 return src_vma_result.Code();
325 }
326 auto src_vma_iter = *src_vma_result;
327 src_vma_iter->second.attribute = MemoryAttribute::Locked;
328 Reprotect(src_vma_iter, VMAPermission::Read);
329
330 // The destination memory region is fine as is, however we need to make it read-only.
331 return ReprotectRange(dst_address, size, VMAPermission::Read);
332}
333
334ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
335 constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
336 const auto src_check_result = CheckRangeState(
337 src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None,
338 VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute);
339
340 if (src_check_result.Failed()) {
341 return src_check_result.Code();
342 }
343
344 // Yes, the kernel only checks the first page of the region.
345 const auto dst_check_result =
346 CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule,
347 MemoryState::FlagModule, VMAPermission::None, VMAPermission::None,
348 MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
349
350 if (dst_check_result.Failed()) {
351 return dst_check_result.Code();
352 }
353
354 const auto dst_memory_state = std::get<MemoryState>(*dst_check_result);
355 const auto dst_contiguous_check_result = CheckRangeState(
356 dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None,
357 VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
358
359 if (dst_contiguous_check_result.Failed()) {
360 return dst_contiguous_check_result.Code();
361 }
362
363 const auto unmap_result = UnmapRange(dst_address, size);
364 if (unmap_result.IsError()) {
365 return unmap_result;
366 }
367
368 // With the mirrored portion unmapped, restore the original region's traits.
369 const auto src_vma_result = CarveVMARange(src_address, size);
370 if (src_vma_result.Failed()) {
371 return src_vma_result.Code();
372 }
373 auto src_vma_iter = *src_vma_result;
374 src_vma_iter->second.state = MemoryState::Heap;
375 src_vma_iter->second.attribute = MemoryAttribute::None;
376 Reprotect(src_vma_iter, VMAPermission::ReadWrite);
377
378 if (dst_memory_state == MemoryState::ModuleCode) {
379 Core::System::GetInstance().InvalidateCpuInstructionCaches();
380 }
381
382 return unmap_result;
383}
384
305MemoryInfo VMManager::QueryMemory(VAddr address) const { 385MemoryInfo VMManager::QueryMemory(VAddr address) const {
306 const auto vma = FindVMA(address); 386 const auto vma = FindVMA(address);
307 MemoryInfo memory_info{}; 387 MemoryInfo memory_info{};
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 6f484b7bf..288eb9450 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -43,6 +43,9 @@ enum class VMAPermission : u8 {
43 ReadExecute = Read | Execute, 43 ReadExecute = Read | Execute,
44 WriteExecute = Write | Execute, 44 WriteExecute = Write | Execute,
45 ReadWriteExecute = Read | Write | Execute, 45 ReadWriteExecute = Read | Write | Execute,
46
47 // Used as a wildcard when checking permissions across memory ranges
48 All = 0xFF,
46}; 49};
47 50
48constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { 51constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) {
@@ -152,6 +155,9 @@ enum class MemoryState : u32 {
152 FlagUncached = 1U << 24, 155 FlagUncached = 1U << 24,
153 FlagCodeMemory = 1U << 25, 156 FlagCodeMemory = 1U << 25,
154 157
158 // Wildcard used in range checking to indicate all states.
159 All = 0xFFFFFFFF,
160
155 // Convenience flag sets to reduce repetition 161 // Convenience flag sets to reduce repetition
156 IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, 162 IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1,
157 163
@@ -415,6 +421,49 @@ public:
415 /// 421 ///
416 ResultVal<VAddr> SetHeapSize(u64 size); 422 ResultVal<VAddr> SetHeapSize(u64 size);
417 423
424 /// Maps a region of memory as code memory.
425 ///
426 /// @param dst_address The base address of the region to create the aliasing memory region.
427 /// @param src_address The base address of the region to be aliased.
428 /// @param size The total amount of memory to map in bytes.
429 ///
430 /// @pre Both memory regions lie within the actual addressable address space.
431 ///
432 /// @post After this function finishes execution, assuming success, then the address range
433 /// [dst_address, dst_address+size) will alias the memory region,
434 /// [src_address, src_address+size).
435 /// <p>
436 /// What this also entails is as follows:
437 /// 1. The aliased region gains the Locked memory attribute.
438 /// 2. The aliased region becomes read-only.
439 /// 3. The aliasing region becomes read-only.
440 /// 4. The aliasing region is created with a memory state of MemoryState::CodeModule.
441 ///
442 ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
443
444 /// Unmaps a region of memory designated as code module memory.
445 ///
446 /// @param dst_address The base address of the memory region aliasing the source memory region.
447 /// @param src_address The base address of the memory region being aliased.
448 /// @param size The size of the memory region to unmap in bytes.
449 ///
450 /// @pre Both memory ranges lie within the actual addressable address space.
451 ///
452 /// @pre The memory region being unmapped has been previously been mapped
453 /// by a call to MapCodeMemory.
454 ///
455 /// @post After execution of the function, if successful. the aliasing memory region
456 /// will be unmapped and the aliased region will have various traits about it
457 /// restored to what they were prior to the original mapping call preceding
458 /// this function call.
459 /// <p>
460 /// What this also entails is as follows:
461 /// 1. The state of the memory region will now indicate a general heap region.
462 /// 2. All memory attributes for the memory region are cleared.
463 /// 3. Memory permissions for the region are restored to user read/write.
464 ///
465 ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
466
418 /// Queries the memory manager for information about the given address. 467 /// Queries the memory manager for information about the given address.
419 /// 468 ///
420 /// @param address The address to query the memory manager about for information. 469 /// @param address The address to query the memory manager about for information.
diff --git a/src/core/hle/kernel/writable_event.h b/src/core/hle/kernel/writable_event.h
index c9068dd3d..d00c92a6b 100644
--- a/src/core/hle/kernel/writable_event.h
+++ b/src/core/hle/kernel/writable_event.h
@@ -37,7 +37,7 @@ public:
37 return name; 37 return name;
38 } 38 }
39 39
40 static const HandleType HANDLE_TYPE = HandleType::WritableEvent; 40 static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent;
41 HandleType GetHandleType() const override { 41 HandleType GetHandleType() const override {
42 return HANDLE_TYPE; 42 return HANDLE_TYPE;
43 } 43 }
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index ab84f5ddc..8a3701151 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -119,10 +119,6 @@ union ResultCode {
119 BitField<0, 9, ErrorModule> module; 119 BitField<0, 9, ErrorModule> module;
120 BitField<9, 13, u32> description; 120 BitField<9, 13, u32> description;
121 121
122 // The last bit of `level` is checked by apps and the kernel to determine if a result code is an
123 // error
124 BitField<31, 1, u32> is_error;
125
126 constexpr explicit ResultCode(u32 raw) : raw(raw) {} 122 constexpr explicit ResultCode(u32 raw) : raw(raw) {}
127 123
128 constexpr ResultCode(ErrorModule module_, u32 description_) 124 constexpr ResultCode(ErrorModule module_, u32 description_)
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 1f8ed265e..ba7d7acbd 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -137,6 +137,7 @@ private:
137class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { 137class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
138public: 138public:
139 IManagerForApplication() : ServiceFramework("IManagerForApplication") { 139 IManagerForApplication() : ServiceFramework("IManagerForApplication") {
140 // clang-format off
140 static const FunctionInfo functions[] = { 141 static const FunctionInfo functions[] = {
141 {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, 142 {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
142 {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, 143 {1, &IManagerForApplication::GetAccountId, "GetAccountId"},
@@ -145,7 +146,10 @@ public:
145 {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"}, 146 {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"},
146 {150, nullptr, "CreateAuthorizationRequest"}, 147 {150, nullptr, "CreateAuthorizationRequest"},
147 {160, nullptr, "StoreOpenContext"}, 148 {160, nullptr, "StoreOpenContext"},
149 {170, nullptr, "LoadNetworkServiceLicenseKindAsync"},
148 }; 150 };
151 // clang-format on
152
149 RegisterHandlers(functions); 153 RegisterHandlers(functions);
150 } 154 }
151 155
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index 5e2030355..d66233cad 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -8,6 +8,7 @@ namespace Service::Account {
8 8
9ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) 9ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") { 10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") {
11 // clang-format off
11 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
12 {0, &ACC_SU::GetUserCount, "GetUserCount"}, 13 {0, &ACC_SU::GetUserCount, "GetUserCount"},
13 {1, &ACC_SU::GetUserExistence, "GetUserExistence"}, 14 {1, &ACC_SU::GetUserExistence, "GetUserExistence"},
@@ -19,6 +20,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
19 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 20 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 21 {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 22 {60, nullptr, "ListOpenContextStoredUsers"},
23 {99, nullptr, "DebugActivateOpenContextRetention"},
22 {100, nullptr, "GetUserRegistrationNotifier"}, 24 {100, nullptr, "GetUserRegistrationNotifier"},
23 {101, nullptr, "GetUserStateChangeNotifier"}, 25 {101, nullptr, "GetUserStateChangeNotifier"},
24 {102, nullptr, "GetBaasAccountManagerForSystemService"}, 26 {102, nullptr, "GetBaasAccountManagerForSystemService"},
@@ -29,6 +31,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
29 {111, nullptr, "ClearSaveDataThumbnail"}, 31 {111, nullptr, "ClearSaveDataThumbnail"},
30 {112, nullptr, "LoadSaveDataThumbnail"}, 32 {112, nullptr, "LoadSaveDataThumbnail"},
31 {113, nullptr, "GetSaveDataThumbnailExistence"}, 33 {113, nullptr, "GetSaveDataThumbnailExistence"},
34 {130, nullptr, "ActivateOpenContextRetention"},
35 {140, nullptr, "ListQualifiedUsers"},
32 {190, nullptr, "GetUserLastOpenedApplication"}, 36 {190, nullptr, "GetUserLastOpenedApplication"},
33 {191, nullptr, "ActivateOpenContextHolder"}, 37 {191, nullptr, "ActivateOpenContextHolder"},
34 {200, nullptr, "BeginUserRegistration"}, 38 {200, nullptr, "BeginUserRegistration"},
@@ -48,6 +52,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
48 {998, nullptr, "DebugSetUserStateClose"}, 52 {998, nullptr, "DebugSetUserStateClose"},
49 {999, nullptr, "DebugSetUserStateOpen"}, 53 {999, nullptr, "DebugSetUserStateOpen"},
50 }; 54 };
55 // clang-format on
56
51 RegisterHandlers(functions); 57 RegisterHandlers(functions);
52} 58}
53 59
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index a4d705b45..182f7c7e5 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -8,6 +8,7 @@ namespace Service::Account {
8 8
9ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) 9ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") { 10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") {
11 // clang-format off
11 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
12 {0, &ACC_U0::GetUserCount, "GetUserCount"}, 13 {0, &ACC_U0::GetUserCount, "GetUserCount"},
13 {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, 14 {1, &ACC_U0::GetUserExistence, "GetUserExistence"},
@@ -19,6 +20,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
19 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 20 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 21 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 22 {60, nullptr, "ListOpenContextStoredUsers"},
23 {99, nullptr, "DebugActivateOpenContextRetention"},
22 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, 24 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
23 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, 25 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
24 {102, nullptr, "AuthenticateApplicationAsync"}, 26 {102, nullptr, "AuthenticateApplicationAsync"},
@@ -27,7 +29,13 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
27 {111, nullptr, "ClearSaveDataThumbnail"}, 29 {111, nullptr, "ClearSaveDataThumbnail"},
28 {120, nullptr, "CreateGuestLoginRequest"}, 30 {120, nullptr, "CreateGuestLoginRequest"},
29 {130, nullptr, "LoadOpenContext"}, 31 {130, nullptr, "LoadOpenContext"},
32 {131, nullptr, "ListOpenContextStoredUsers"},
33 {140, nullptr, "InitializeApplicationInfo"},
34 {141, nullptr, "ListQualifiedUsers"},
35 {150, nullptr, "IsUserAccountSwitchLocked"},
30 }; 36 };
37 // clang-format on
38
31 RegisterHandlers(functions); 39 RegisterHandlers(functions);
32} 40}
33 41
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index 8fffc93b5..2dd17d935 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -8,6 +8,7 @@ namespace Service::Account {
8 8
9ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) 9ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") { 10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") {
11 // clang-format off
11 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
12 {0, &ACC_U1::GetUserCount, "GetUserCount"}, 13 {0, &ACC_U1::GetUserCount, "GetUserCount"},
13 {1, &ACC_U1::GetUserExistence, "GetUserExistence"}, 14 {1, &ACC_U1::GetUserExistence, "GetUserExistence"},
@@ -19,6 +20,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
19 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 20 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 21 {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 22 {60, nullptr, "ListOpenContextStoredUsers"},
23 {99, nullptr, "DebugActivateOpenContextRetention"},
22 {100, nullptr, "GetUserRegistrationNotifier"}, 24 {100, nullptr, "GetUserRegistrationNotifier"},
23 {101, nullptr, "GetUserStateChangeNotifier"}, 25 {101, nullptr, "GetUserStateChangeNotifier"},
24 {102, nullptr, "GetBaasAccountManagerForSystemService"}, 26 {102, nullptr, "GetBaasAccountManagerForSystemService"},
@@ -29,12 +31,16 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
29 {111, nullptr, "ClearSaveDataThumbnail"}, 31 {111, nullptr, "ClearSaveDataThumbnail"},
30 {112, nullptr, "LoadSaveDataThumbnail"}, 32 {112, nullptr, "LoadSaveDataThumbnail"},
31 {113, nullptr, "GetSaveDataThumbnailExistence"}, 33 {113, nullptr, "GetSaveDataThumbnailExistence"},
34 {130, nullptr, "ActivateOpenContextRetention"},
35 {140, nullptr, "ListQualifiedUsers"},
32 {190, nullptr, "GetUserLastOpenedApplication"}, 36 {190, nullptr, "GetUserLastOpenedApplication"},
33 {191, nullptr, "ActivateOpenContextHolder"}, 37 {191, nullptr, "ActivateOpenContextHolder"},
34 {997, nullptr, "DebugInvalidateTokenCacheForUser"}, 38 {997, nullptr, "DebugInvalidateTokenCacheForUser"},
35 {998, nullptr, "DebugSetUserStateClose"}, 39 {998, nullptr, "DebugSetUserStateClose"},
36 {999, nullptr, "DebugSetUserStateOpen"}, 40 {999, nullptr, "DebugSetUserStateOpen"},
37 }; 41 };
42 // clang-format on
43
38 RegisterHandlers(functions); 44 RegisterHandlers(functions);
39} 45}
40 46
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index d31ab7970..1aa4ce1ac 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -13,7 +13,7 @@
13#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/readable_event.h" 15#include "core/hle/kernel/readable_event.h"
16#include "core/hle/kernel/shared_memory.h" 16#include "core/hle/kernel/transfer_memory.h"
17#include "core/hle/kernel/writable_event.h" 17#include "core/hle/kernel/writable_event.h"
18#include "core/hle/service/acc/profile_manager.h" 18#include "core/hle/service/acc/profile_manager.h"
19#include "core/hle/service/am/am.h" 19#include "core/hle/service/am/am.h"
@@ -224,6 +224,7 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
224 {20, nullptr, "InvalidateTransitionLayer"}, 224 {20, nullptr, "InvalidateTransitionLayer"},
225 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, 225 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
226 {40, nullptr, "GetAppletResourceUsageInfo"}, 226 {40, nullptr, "GetAppletResourceUsageInfo"},
227 {41, nullptr, "SetCpuBoostModeForApplet"},
227 }; 228 };
228 // clang-format on 229 // clang-format on
229 230
@@ -256,6 +257,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
256 {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, 257 {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
257 {41, nullptr, "IsSystemBufferSharingEnabled"}, 258 {41, nullptr, "IsSystemBufferSharingEnabled"},
258 {42, nullptr, "GetSystemSharedLayerHandle"}, 259 {42, nullptr, "GetSystemSharedLayerHandle"},
260 {43, nullptr, "GetSystemSharedBufferHandle"},
259 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, 261 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
260 {51, nullptr, "ApproveToDisplay"}, 262 {51, nullptr, "ApproveToDisplay"},
261 {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, 263 {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
@@ -269,9 +271,11 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
269 {68, nullptr, "SetAutoSleepDisabled"}, 271 {68, nullptr, "SetAutoSleepDisabled"},
270 {69, nullptr, "IsAutoSleepDisabled"}, 272 {69, nullptr, "IsAutoSleepDisabled"},
271 {70, nullptr, "ReportMultimediaError"}, 273 {70, nullptr, "ReportMultimediaError"},
274 {71, nullptr, "GetCurrentIlluminanceEx"},
272 {80, nullptr, "SetWirelessPriorityMode"}, 275 {80, nullptr, "SetWirelessPriorityMode"},
273 {90, nullptr, "GetAccumulatedSuspendedTickValue"}, 276 {90, nullptr, "GetAccumulatedSuspendedTickValue"},
274 {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, 277 {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
278 {100, nullptr, "SetAlbumImageTakenNotificationEnabled"},
275 {1000, nullptr, "GetDebugStorageChannel"}, 279 {1000, nullptr, "GetDebugStorageChannel"},
276 }; 280 };
277 // clang-format on 281 // clang-format on
@@ -363,8 +367,6 @@ void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestCo
363void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { 367void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
364 // Takes 3 input u8s with each field located immediately after the previous 368 // Takes 3 input u8s with each field located immediately after the previous
365 // u8, these are bool flags. No output. 369 // u8, these are bool flags. No output.
366 LOG_WARNING(Service_AM, "(STUBBED) called");
367
368 IPC::RequestParser rp{ctx}; 370 IPC::RequestParser rp{ctx};
369 371
370 struct FocusHandlingModeParams { 372 struct FocusHandlingModeParams {
@@ -372,7 +374,10 @@ void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
372 u8 unknown1; 374 u8 unknown1;
373 u8 unknown2; 375 u8 unknown2;
374 }; 376 };
375 auto flags = rp.PopRaw<FocusHandlingModeParams>(); 377 const auto flags = rp.PopRaw<FocusHandlingModeParams>();
378
379 LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}",
380 flags.unknown0, flags.unknown1, flags.unknown2);
376 381
377 IPC::ResponseBuilder rb{ctx, 2}; 382 IPC::ResponseBuilder rb{ctx, 2};
378 rb.Push(RESULT_SUCCESS); 383 rb.Push(RESULT_SUCCESS);
@@ -515,11 +520,20 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q
515 {50, nullptr, "IsVrModeEnabled"}, 520 {50, nullptr, "IsVrModeEnabled"},
516 {51, nullptr, "SetVrModeEnabled"}, 521 {51, nullptr, "SetVrModeEnabled"},
517 {52, nullptr, "SwitchLcdBacklight"}, 522 {52, nullptr, "SwitchLcdBacklight"},
523 {53, nullptr, "BeginVrModeEx"},
524 {54, nullptr, "EndVrModeEx"},
518 {55, nullptr, "IsInControllerFirmwareUpdateSection"}, 525 {55, nullptr, "IsInControllerFirmwareUpdateSection"},
519 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, 526 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
520 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, 527 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
521 {62, nullptr, "GetHdcpAuthenticationState"}, 528 {62, nullptr, "GetHdcpAuthenticationState"},
522 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, 529 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
530 {64, nullptr, "SetTvPowerStateMatchingMode"},
531 {65, nullptr, "GetApplicationIdByContentActionName"},
532 {66, nullptr, "SetCpuBoostMode"},
533 {80, nullptr, "PerformSystemButtonPressingIfInFocus"},
534 {90, nullptr, "SetPerformanceConfigurationChangedNotification"},
535 {91, nullptr, "GetCurrentPerformanceConfiguration"},
536 {200, nullptr, "GetOperationModeSystemInfo"},
523 }; 537 };
524 // clang-format on 538 // clang-format on
525 539
@@ -931,19 +945,19 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
931 rp.SetCurrentOffset(3); 945 rp.SetCurrentOffset(3);
932 const auto handle{rp.Pop<Kernel::Handle>()}; 946 const auto handle{rp.Pop<Kernel::Handle>()};
933 947
934 const auto shared_mem = 948 const auto transfer_mem =
935 Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>( 949 Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(
936 handle); 950 handle);
937 951
938 if (shared_mem == nullptr) { 952 if (transfer_mem == nullptr) {
939 LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); 953 LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle);
940 IPC::ResponseBuilder rb{ctx, 2}; 954 IPC::ResponseBuilder rb{ctx, 2};
941 rb.Push(ResultCode(-1)); 955 rb.Push(ResultCode(-1));
942 return; 956 return;
943 } 957 }
944 958
945 const u8* mem_begin = shared_mem->GetPointer(); 959 const u8* const mem_begin = transfer_mem->GetPointer();
946 const u8* mem_end = mem_begin + shared_mem->GetSize(); 960 const u8* const mem_end = mem_begin + transfer_mem->GetSize();
947 std::vector<u8> memory{mem_begin, mem_end}; 961 std::vector<u8> memory{mem_begin, mem_end};
948 962
949 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 963 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -959,6 +973,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
959 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, 973 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
960 {12, nullptr, "CreateApplicationAndRequestToStart"}, 974 {12, nullptr, "CreateApplicationAndRequestToStart"},
961 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, 975 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
976 {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"},
977 {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"},
962 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, 978 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
963 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, 979 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
964 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, 980 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
@@ -1232,6 +1248,7 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
1232 {2, nullptr, "StartSleepSequence"}, 1248 {2, nullptr, "StartSleepSequence"},
1233 {3, nullptr, "StartShutdownSequence"}, 1249 {3, nullptr, "StartShutdownSequence"},
1234 {4, nullptr, "StartRebootSequence"}, 1250 {4, nullptr, "StartRebootSequence"},
1251 {9, nullptr, "IsAutoPowerDownRequested"},
1235 {10, nullptr, "LoadAndApplyIdlePolicySettings"}, 1252 {10, nullptr, "LoadAndApplyIdlePolicySettings"},
1236 {11, nullptr, "NotifyCecSettingsChanged"}, 1253 {11, nullptr, "NotifyCecSettingsChanged"},
1237 {12, nullptr, "SetDefaultHomeButtonLongPressTime"}, 1254 {12, nullptr, "SetDefaultHomeButtonLongPressTime"},
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index b888f861d..488add8e7 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -16,6 +16,7 @@ public:
16 std::shared_ptr<AppletMessageQueue> msg_queue) 16 std::shared_ptr<AppletMessageQueue> msg_queue)
17 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), 17 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
18 msg_queue(std::move(msg_queue)) { 18 msg_queue(std::move(msg_queue)) {
19 // clang-format off
19 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
20 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 21 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
21 {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, 22 {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
@@ -25,8 +26,11 @@ public:
25 {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"}, 26 {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
26 {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, 27 {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
27 {20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"}, 28 {20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"},
29 {21, nullptr, "GetAppletCommonFunctions"},
28 {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, 30 {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
29 }; 31 };
32 // clang-format on
33
30 RegisterHandlers(functions); 34 RegisterHandlers(functions);
31 } 35 }
32 36
@@ -113,6 +117,7 @@ public:
113 std::shared_ptr<AppletMessageQueue> msg_queue) 117 std::shared_ptr<AppletMessageQueue> msg_queue)
114 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), 118 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
115 msg_queue(std::move(msg_queue)) { 119 msg_queue(std::move(msg_queue)) {
120 // clang-format off
116 static const FunctionInfo functions[] = { 121 static const FunctionInfo functions[] = {
117 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 122 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
118 {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, 123 {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
@@ -124,8 +129,11 @@ public:
124 {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, 129 {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
125 {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, 130 {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
126 {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"}, 131 {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"},
132 {23, nullptr, "GetAppletCommonFunctions"},
127 {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, 133 {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
128 }; 134 };
135 // clang-format on
136
129 RegisterHandlers(functions); 137 RegisterHandlers(functions);
130 } 138 }
131 139
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 9b0aa7f5f..7e17df98a 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -86,7 +86,7 @@ static FileSys::VirtualFile GetManualRomFS() {
86 if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success) 86 if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success)
87 return out; 87 return out;
88 88
89 const auto& installed{FileSystem::GetUnionContents()}; 89 const auto& installed{Core::System::GetInstance().GetContentProvider()};
90 const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(), 90 const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(),
91 FileSys::ContentRecordType::Manual); 91 FileSys::ContentRecordType::Manual);
92 92
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index b506bc3dd..51d8c26b4 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -33,11 +33,11 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
33 33
34static std::vector<u64> AccumulateAOCTitleIDs() { 34static std::vector<u64> AccumulateAOCTitleIDs() {
35 std::vector<u64> add_on_content; 35 std::vector<u64> add_on_content;
36 const auto rcu = FileSystem::GetUnionContents(); 36 const auto& rcu = Core::System::GetInstance().GetContentProvider();
37 const auto list = 37 const auto list =
38 rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); 38 rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
39 std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), 39 std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
40 [](const FileSys::RegisteredCacheEntry& rce) { return rce.title_id; }); 40 [](const FileSys::ContentProviderEntry& rce) { return rce.title_id; });
41 add_on_content.erase( 41 add_on_content.erase(
42 std::remove_if( 42 std::remove_if(
43 add_on_content.begin(), add_on_content.end(), 43 add_on_content.begin(), add_on_content.end(),
@@ -50,6 +50,7 @@ static std::vector<u64> AccumulateAOCTitleIDs() {
50} 50}
51 51
52AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) { 52AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) {
53 // clang-format off
53 static const FunctionInfo functions[] = { 54 static const FunctionInfo functions[] = {
54 {0, nullptr, "CountAddOnContentByApplicationId"}, 55 {0, nullptr, "CountAddOnContentByApplicationId"},
55 {1, nullptr, "ListAddOnContentByApplicationId"}, 56 {1, nullptr, "ListAddOnContentByApplicationId"},
@@ -60,7 +61,10 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
60 {6, nullptr, "PrepareAddOnContentByApplicationId"}, 61 {6, nullptr, "PrepareAddOnContentByApplicationId"},
61 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, 62 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
62 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, 63 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
64 {100, nullptr, "CreateEcPurchasedEventManager"},
63 }; 65 };
66 // clang-format on
67
64 RegisterHandlers(functions); 68 RegisterHandlers(functions);
65 69
66 auto& kernel = Core::System::GetInstance().Kernel(); 70 auto& kernel = Core::System::GetInstance().Kernel();
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index fcacbab72..d058c0245 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -87,6 +87,8 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
87 {3, nullptr, "GetLastThrottlingState"}, 87 {3, nullptr, "GetLastThrottlingState"},
88 {4, nullptr, "ClearLastThrottlingState"}, 88 {4, nullptr, "ClearLastThrottlingState"},
89 {5, nullptr, "LoadAndApplySettings"}, 89 {5, nullptr, "LoadAndApplySettings"},
90 {6, nullptr, "SetCpuBoostMode"},
91 {7, nullptr, "GetCurrentPerformanceConfiguration"},
90 }; 92 };
91 // clang-format on 93 // clang-format on
92 94
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 088410564..d7f1d348d 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -2,9 +2,6 @@
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 "common/logging/log.h"
6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/hle_ipc.h"
8#include "core/hle/service/audio/audin_u.h" 5#include "core/hle/service/audio/audin_u.h"
9 6
10namespace Service::Audio { 7namespace Service::Audio {
@@ -28,12 +25,12 @@ public:
28 {11, nullptr, "GetAudioInBufferCount"}, 25 {11, nullptr, "GetAudioInBufferCount"},
29 {12, nullptr, "SetAudioInDeviceGain"}, 26 {12, nullptr, "SetAudioInDeviceGain"},
30 {13, nullptr, "GetAudioInDeviceGain"}, 27 {13, nullptr, "GetAudioInDeviceGain"},
28 {14, nullptr, "FlushAudioInBuffers"},
31 }; 29 };
32 // clang-format on 30 // clang-format on
33 31
34 RegisterHandlers(functions); 32 RegisterHandlers(functions);
35 } 33 }
36 ~IAudioIn() = default;
37}; 34};
38 35
39AudInU::AudInU() : ServiceFramework("audin:u") { 36AudInU::AudInU() : ServiceFramework("audin:u") {
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 21f5e64c7..12875fb42 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -44,7 +44,7 @@ public:
44 std::string&& unique_name) 44 std::string&& unique_name)
45 : ServiceFramework("IAudioOut"), audio_core(audio_core), 45 : ServiceFramework("IAudioOut"), audio_core(audio_core),
46 device_name(std::move(device_name)), audio_params(audio_params) { 46 device_name(std::move(device_name)), audio_params(audio_params) {
47 47 // clang-format off
48 static const FunctionInfo functions[] = { 48 static const FunctionInfo functions[] = {
49 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 49 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
50 {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, 50 {1, &IAudioOut::StartAudioOut, "StartAudioOut"},
@@ -58,7 +58,10 @@ public:
58 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, 58 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
59 {10, nullptr, "GetAudioOutPlayedSampleCount"}, 59 {10, nullptr, "GetAudioOutPlayedSampleCount"},
60 {11, nullptr, "FlushAudioOutBuffers"}, 60 {11, nullptr, "FlushAudioOutBuffers"},
61 {12, nullptr, "SetAudioOutVolume"},
62 {13, nullptr, "GetAudioOutVolume"},
61 }; 63 };
64 // clang-format on
62 RegisterHandlers(functions); 65 RegisterHandlers(functions);
63 66
64 // This is the event handle used to check if the audio buffer was released 67 // This is the event handle used to check if the audio buffer was released
@@ -150,7 +153,6 @@ private:
150 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { 153 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
151 LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); 154 LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
152 155
153 IPC::RequestParser rp{ctx};
154 const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; 156 const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
155 const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; 157 const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
156 158
@@ -194,12 +196,9 @@ private:
194void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { 196void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
195 LOG_DEBUG(Service_Audio, "called"); 197 LOG_DEBUG(Service_Audio, "called");
196 198
197 IPC::RequestParser rp{ctx};
198
199 ctx.WriteBuffer(DefaultDevice); 199 ctx.WriteBuffer(DefaultDevice);
200 200
201 IPC::ResponseBuilder rb{ctx, 3}; 201 IPC::ResponseBuilder rb{ctx, 3};
202
203 rb.Push(RESULT_SUCCESS); 202 rb.Push(RESULT_SUCCESS);
204 rb.Push<u32>(1); // Amount of audio devices 203 rb.Push<u32>(1); // Amount of audio devices
205} 204}
diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp
index 6956a2e64..1a5aed9ed 100644
--- a/src/core/hle/service/audio/audrec_u.cpp
+++ b/src/core/hle/service/audio/audrec_u.cpp
@@ -2,9 +2,6 @@
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 "common/logging/log.h"
6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/hle_ipc.h"
8#include "core/hle/service/audio/audrec_u.h" 5#include "core/hle/service/audio/audrec_u.h"
9 6
10namespace Service::Audio { 7namespace Service::Audio {
@@ -30,7 +27,6 @@ public:
30 27
31 RegisterHandlers(functions); 28 RegisterHandlers(functions);
32 } 29 }
33 ~IFinalOutputRecorder() = default;
34}; 30};
35 31
36AudRecU::AudRecU() : ServiceFramework("audrec:u") { 32AudRecU::AudRecU() : ServiceFramework("audrec:u") {
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index c9de10a24..1dde6edb7 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -10,6 +10,7 @@
10#include "common/alignment.h" 10#include "common/alignment.h"
11#include "common/common_funcs.h" 11#include "common/common_funcs.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/string_util.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/hle_ipc.h" 16#include "core/hle/kernel/hle_ipc.h"
@@ -184,7 +185,6 @@ public:
184private: 185private:
185 void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { 186 void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
186 LOG_WARNING(Service_Audio, "(STUBBED) called"); 187 LOG_WARNING(Service_Audio, "(STUBBED) called");
187 IPC::RequestParser rp{ctx};
188 188
189 constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; 189 constexpr std::array<char, 15> audio_interface{{"AudioInterface"}};
190 ctx.WriteBuffer(audio_interface); 190 ctx.WriteBuffer(audio_interface);
@@ -195,13 +195,13 @@ private:
195 } 195 }
196 196
197 void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { 197 void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
198 LOG_WARNING(Service_Audio, "(STUBBED) called");
199
200 IPC::RequestParser rp{ctx}; 198 IPC::RequestParser rp{ctx};
201 f32 volume = static_cast<f32>(rp.Pop<u32>()); 199 const f32 volume = rp.Pop<f32>();
202 200
203 auto file_buffer = ctx.ReadBuffer(); 201 const auto device_name_buffer = ctx.ReadBuffer();
204 auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); 202 const std::string name = Common::StringFromBuffer(device_name_buffer);
203
204 LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume);
205 205
206 IPC::ResponseBuilder rb{ctx, 2}; 206 IPC::ResponseBuilder rb{ctx, 2};
207 rb.Push(RESULT_SUCCESS); 207 rb.Push(RESULT_SUCCESS);
@@ -209,7 +209,6 @@ private:
209 209
210 void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { 210 void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
211 LOG_WARNING(Service_Audio, "(STUBBED) called"); 211 LOG_WARNING(Service_Audio, "(STUBBED) called");
212 IPC::RequestParser rp{ctx};
213 212
214 constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; 213 constexpr std::array<char, 12> audio_interface{{"AudioDevice"}};
215 ctx.WriteBuffer(audio_interface); 214 ctx.WriteBuffer(audio_interface);
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index 59ef603e1..974ff8e1a 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -154,7 +154,8 @@ public:
154 {96, nullptr, "GetLeHidEventInfo"}, 154 {96, nullptr, "GetLeHidEventInfo"},
155 {97, nullptr, "RegisterBleHidEvent"}, 155 {97, nullptr, "RegisterBleHidEvent"},
156 {98, nullptr, "SetLeScanParameter"}, 156 {98, nullptr, "SetLeScanParameter"},
157 {256, nullptr, "GetIsManufacturingMode"} 157 {256, nullptr, "GetIsManufacturingMode"},
158 {257, nullptr, "EmulateBluetoothCrash"},
158 }; 159 };
159 // clang-format on 160 // clang-format on
160 161
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp
index ae7b0720b..907f464ab 100644
--- a/src/core/hle/service/caps/caps.cpp
+++ b/src/core/hle/service/caps/caps.cpp
@@ -15,32 +15,41 @@ public:
15 explicit CAPS_A() : ServiceFramework{"caps:a"} { 15 explicit CAPS_A() : ServiceFramework{"caps:a"} {
16 // clang-format off 16 // clang-format off
17 static const FunctionInfo functions[] = { 17 static const FunctionInfo functions[] = {
18 {0, nullptr, "Unknown1"}, 18 {0, nullptr, "GetAlbumFileCount"},
19 {1, nullptr, "Unknown2"}, 19 {1, nullptr, "GetAlbumFileList"},
20 {2, nullptr, "Unknown3"}, 20 {2, nullptr, "LoadAlbumFile"},
21 {3, nullptr, "Unknown4"}, 21 {3, nullptr, "DeleteAlbumFile"},
22 {4, nullptr, "Unknown5"}, 22 {4, nullptr, "StorageCopyAlbumFile"},
23 {5, nullptr, "Unknown6"}, 23 {5, nullptr, "IsAlbumMounted"},
24 {6, nullptr, "Unknown7"}, 24 {6, nullptr, "GetAlbumUsage"},
25 {7, nullptr, "Unknown8"}, 25 {7, nullptr, "GetAlbumFileSize"},
26 {8, nullptr, "Unknown9"}, 26 {8, nullptr, "LoadAlbumFileThumbnail"},
27 {9, nullptr, "Unknown10"}, 27 {9, nullptr, "LoadAlbumScreenShotImage"},
28 {10, nullptr, "Unknown11"}, 28 {10, nullptr, "LoadAlbumScreenShotThumbnailImage"},
29 {11, nullptr, "Unknown12"}, 29 {11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"},
30 {12, nullptr, "Unknown13"}, 30 {12, nullptr, "Unknown12"},
31 {13, nullptr, "Unknown14"}, 31 {13, nullptr, "Unknown13"},
32 {14, nullptr, "Unknown15"}, 32 {14, nullptr, "Unknown14"},
33 {301, nullptr, "Unknown16"}, 33 {15, nullptr, "Unknown15"},
34 {401, nullptr, "Unknown17"}, 34 {16, nullptr, "Unknown16"},
35 {501, nullptr, "Unknown18"}, 35 {17, nullptr, "Unknown17"},
36 {1001, nullptr, "Unknown19"}, 36 {18, nullptr, "Unknown18"},
37 {1002, nullptr, "Unknown20"}, 37 {202, nullptr, "SaveEditedScreenShot"},
38 {8001, nullptr, "Unknown21"}, 38 {301, nullptr, "GetLastThumbnail"},
39 {8002, nullptr, "Unknown22"}, 39 {401, nullptr, "GetAutoSavingStorage"},
40 {8011, nullptr, "Unknown23"}, 40 {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
41 {8012, nullptr, "Unknown24"}, 41 {1001, nullptr, "Unknown1001"},
42 {8021, nullptr, "Unknown25"}, 42 {1002, nullptr, "Unknown1002"},
43 {10011, nullptr, "Unknown26"}, 43 {1003, nullptr, "Unknown1003"},
44 {8001, nullptr, "ForceAlbumUnmounted"},
45 {8002, nullptr, "ResetAlbumMountStatus"},
46 {8011, nullptr, "RefreshAlbumCache"},
47 {8012, nullptr, "GetAlbumCache"},
48 {8013, nullptr, "Unknown8013"},
49 {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"},
50 {10011, nullptr, "SetInternalErrorConversionEnabled"},
51 {50000, nullptr, "Unknown50000"},
52 {60002, nullptr, "Unknown60002"},
44 }; 53 };
45 // clang-format on 54 // clang-format on
46 55
@@ -53,16 +62,17 @@ public:
53 explicit CAPS_C() : ServiceFramework{"caps:c"} { 62 explicit CAPS_C() : ServiceFramework{"caps:c"} {
54 // clang-format off 63 // clang-format off
55 static const FunctionInfo functions[] = { 64 static const FunctionInfo functions[] = {
56 {2001, nullptr, "Unknown1"}, 65 {33, nullptr, "Unknown33"},
57 {2002, nullptr, "Unknown2"}, 66 {2001, nullptr, "Unknown2001"},
58 {2011, nullptr, "Unknown3"}, 67 {2002, nullptr, "Unknown2002"},
59 {2012, nullptr, "Unknown4"}, 68 {2011, nullptr, "Unknown2011"},
60 {2013, nullptr, "Unknown5"}, 69 {2012, nullptr, "Unknown2012"},
61 {2014, nullptr, "Unknown6"}, 70 {2013, nullptr, "Unknown2013"},
62 {2101, nullptr, "Unknown7"}, 71 {2014, nullptr, "Unknown2014"},
63 {2102, nullptr, "Unknown8"}, 72 {2101, nullptr, "Unknown2101"},
64 {2201, nullptr, "Unknown9"}, 73 {2102, nullptr, "Unknown2102"},
65 {2301, nullptr, "Unknown10"}, 74 {2201, nullptr, "Unknown2201"},
75 {2301, nullptr, "Unknown2301"},
66 }; 76 };
67 // clang-format on 77 // clang-format on
68 78
@@ -127,11 +137,18 @@ public:
127 explicit CAPS_U() : ServiceFramework{"caps:u"} { 137 explicit CAPS_U() : ServiceFramework{"caps:u"} {
128 // clang-format off 138 // clang-format off
129 static const FunctionInfo functions[] = { 139 static const FunctionInfo functions[] = {
140 {32, nullptr, "SetShimLibraryVersion"},
130 {102, nullptr, "GetAlbumFileListByAruid"}, 141 {102, nullptr, "GetAlbumFileListByAruid"},
131 {103, nullptr, "DeleteAlbumFileByAruid"}, 142 {103, nullptr, "DeleteAlbumFileByAruid"},
132 {104, nullptr, "GetAlbumFileSizeByAruid"}, 143 {104, nullptr, "GetAlbumFileSizeByAruid"},
144 {105, nullptr, "DeleteAlbumFileByAruidForDebug"},
133 {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, 145 {110, nullptr, "LoadAlbumScreenShotImageByAruid"},
134 {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, 146 {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"},
147 {130, nullptr, "PrecheckToCreateContentsByAruid"},
148 {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
149 {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
150 {142, nullptr, "GetAlbumFileList3AaeAruid"},
151 {143, nullptr, "GetAlbumFileList4AaeUidAruid"},
135 {60002, nullptr, "OpenAccessorSessionForApplication"}, 152 {60002, nullptr, "OpenAccessorSessionForApplication"},
136 }; 153 };
137 // clang-format on 154 // clang-format on
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index c6da2df43..1ebfeb4bf 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -197,13 +197,16 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
197 197
198ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, 198ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
199 FileSys::Mode mode) const { 199 FileSys::Mode mode) const {
200 std::string path(FileUtil::SanitizePath(path_)); 200 const std::string path(FileUtil::SanitizePath(path_));
201 auto npath = path; 201 std::string_view npath = path;
202 while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\')) 202 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
203 npath = npath.substr(1); 203 npath.remove_prefix(1);
204 }
205
204 auto file = backing->GetFileRelative(npath); 206 auto file = backing->GetFileRelative(npath);
205 if (file == nullptr) 207 if (file == nullptr) {
206 return FileSys::ERROR_PATH_NOT_FOUND; 208 return FileSys::ERROR_PATH_NOT_FOUND;
209 }
207 210
208 if (mode == FileSys::Mode::Append) { 211 if (mode == FileSys::Mode::Append) {
209 return MakeResult<FileSys::VirtualFile>( 212 return MakeResult<FileSys::VirtualFile>(
@@ -319,15 +322,15 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
319} 322}
320 323
321ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, 324ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
322 FileSys::SaveDataDescriptor save_struct) { 325 const FileSys::SaveDataDescriptor& descriptor) {
323 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", 326 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
324 static_cast<u8>(space), save_struct.DebugInfo()); 327 static_cast<u8>(space), descriptor.DebugInfo());
325 328
326 if (save_data_factory == nullptr) { 329 if (save_data_factory == nullptr) {
327 return FileSys::ERROR_ENTITY_NOT_FOUND; 330 return FileSys::ERROR_ENTITY_NOT_FOUND;
328 } 331 }
329 332
330 return save_data_factory->Open(space, save_struct); 333 return save_data_factory->Open(space, descriptor);
331} 334}
332 335
333ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { 336ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
@@ -388,11 +391,6 @@ void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
388 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); 391 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
389} 392}
390 393
391FileSys::RegisteredCacheUnion GetUnionContents() {
392 return FileSys::RegisteredCacheUnion{
393 {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
394}
395
396FileSys::RegisteredCache* GetSystemNANDContents() { 394FileSys::RegisteredCache* GetSystemNANDContents() {
397 LOG_TRACE(Service_FS, "Opening System NAND Contents"); 395 LOG_TRACE(Service_FS, "Opening System NAND Contents");
398 396
@@ -457,6 +455,10 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
457 if (bis_factory == nullptr) { 455 if (bis_factory == nullptr) {
458 bis_factory = 456 bis_factory =
459 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); 457 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
458 Core::System::GetInstance().RegisterContentProvider(
459 FileSys::ContentProviderUnionSlot::SysNAND, bis_factory->GetSystemNANDContents());
460 Core::System::GetInstance().RegisterContentProvider(
461 FileSys::ContentProviderUnionSlot::UserNAND, bis_factory->GetUserNANDContents());
460 } 462 }
461 463
462 if (save_data_factory == nullptr) { 464 if (save_data_factory == nullptr) {
@@ -465,6 +467,8 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
465 467
466 if (sdmc_factory == nullptr) { 468 if (sdmc_factory == nullptr) {
467 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 469 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
470 Core::System::GetInstance().RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
471 sdmc_factory->GetSDMCContents());
468 } 472 }
469} 473}
470 474
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 6fd5e7b23..6481f237c 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -46,7 +46,7 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
46ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, 46ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
47 FileSys::ContentRecordType type); 47 FileSys::ContentRecordType type);
48ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, 48ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
49 FileSys::SaveDataDescriptor save_struct); 49 const FileSys::SaveDataDescriptor& descriptor);
50ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); 50ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
51ResultVal<FileSys::VirtualDir> OpenSDMC(); 51ResultVal<FileSys::VirtualDir> OpenSDMC();
52 52
@@ -54,8 +54,6 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
54void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, 54void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
55 FileSys::SaveDataSize new_value); 55 FileSys::SaveDataSize new_value);
56 56
57FileSys::RegisteredCacheUnion GetUnionContents();
58
59FileSys::RegisteredCache* GetSystemNANDContents(); 57FileSys::RegisteredCache* GetSystemNANDContents();
60FileSys::RegisteredCache* GetUserNANDContents(); 58FileSys::RegisteredCache* GetUserNANDContents();
61FileSys::RegisteredCache* GetSDMCContents(); 59FileSys::RegisteredCache* GetSDMCContents();
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index f03fb629c..e7df8fd98 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -115,11 +115,12 @@ private:
115 115
116 void Read(Kernel::HLERequestContext& ctx) { 116 void Read(Kernel::HLERequestContext& ctx) {
117 IPC::RequestParser rp{ctx}; 117 IPC::RequestParser rp{ctx};
118 const u64 unk = rp.Pop<u64>(); 118 const u64 option = rp.Pop<u64>();
119 const s64 offset = rp.Pop<s64>(); 119 const s64 offset = rp.Pop<s64>();
120 const s64 length = rp.Pop<s64>(); 120 const s64 length = rp.Pop<s64>();
121 121
122 LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); 122 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
123 length);
123 124
124 // Error checking 125 // Error checking
125 if (length < 0) { 126 if (length < 0) {
@@ -148,11 +149,12 @@ private:
148 149
149 void Write(Kernel::HLERequestContext& ctx) { 150 void Write(Kernel::HLERequestContext& ctx) {
150 IPC::RequestParser rp{ctx}; 151 IPC::RequestParser rp{ctx};
151 const u64 unk = rp.Pop<u64>(); 152 const u64 option = rp.Pop<u64>();
152 const s64 offset = rp.Pop<s64>(); 153 const s64 offset = rp.Pop<s64>();
153 const s64 length = rp.Pop<s64>(); 154 const s64 length = rp.Pop<s64>();
154 155
155 LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); 156 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
157 length);
156 158
157 // Error checking 159 // Error checking
158 if (length < 0) { 160 if (length < 0) {
@@ -250,10 +252,7 @@ private:
250 u64 next_entry_index = 0; 252 u64 next_entry_index = 0;
251 253
252 void Read(Kernel::HLERequestContext& ctx) { 254 void Read(Kernel::HLERequestContext& ctx) {
253 IPC::RequestParser rp{ctx}; 255 LOG_DEBUG(Service_FS, "called.");
254 const u64 unk = rp.Pop<u64>();
255
256 LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk);
257 256
258 // Calculate how many entries we can fit in the output buffer 257 // Calculate how many entries we can fit in the output buffer
259 const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); 258 const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
@@ -315,61 +314,53 @@ public:
315 void CreateFile(Kernel::HLERequestContext& ctx) { 314 void CreateFile(Kernel::HLERequestContext& ctx) {
316 IPC::RequestParser rp{ctx}; 315 IPC::RequestParser rp{ctx};
317 316
318 auto file_buffer = ctx.ReadBuffer(); 317 const auto file_buffer = ctx.ReadBuffer();
319 std::string name = Common::StringFromBuffer(file_buffer); 318 const std::string name = Common::StringFromBuffer(file_buffer);
320 319
321 u64 mode = rp.Pop<u64>(); 320 const u64 mode = rp.Pop<u64>();
322 u32 size = rp.Pop<u32>(); 321 const u32 size = rp.Pop<u32>();
323 322
324 LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); 323 LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, mode, size);
325 324
326 IPC::ResponseBuilder rb{ctx, 2}; 325 IPC::ResponseBuilder rb{ctx, 2};
327 rb.Push(backend.CreateFile(name, size)); 326 rb.Push(backend.CreateFile(name, size));
328 } 327 }
329 328
330 void DeleteFile(Kernel::HLERequestContext& ctx) { 329 void DeleteFile(Kernel::HLERequestContext& ctx) {
331 IPC::RequestParser rp{ctx}; 330 const auto file_buffer = ctx.ReadBuffer();
332 331 const std::string name = Common::StringFromBuffer(file_buffer);
333 auto file_buffer = ctx.ReadBuffer();
334 std::string name = Common::StringFromBuffer(file_buffer);
335 332
336 LOG_DEBUG(Service_FS, "called file {}", name); 333 LOG_DEBUG(Service_FS, "called. file={}", name);
337 334
338 IPC::ResponseBuilder rb{ctx, 2}; 335 IPC::ResponseBuilder rb{ctx, 2};
339 rb.Push(backend.DeleteFile(name)); 336 rb.Push(backend.DeleteFile(name));
340 } 337 }
341 338
342 void CreateDirectory(Kernel::HLERequestContext& ctx) { 339 void CreateDirectory(Kernel::HLERequestContext& ctx) {
343 IPC::RequestParser rp{ctx}; 340 const auto file_buffer = ctx.ReadBuffer();
344 341 const std::string name = Common::StringFromBuffer(file_buffer);
345 auto file_buffer = ctx.ReadBuffer();
346 std::string name = Common::StringFromBuffer(file_buffer);
347 342
348 LOG_DEBUG(Service_FS, "called directory {}", name); 343 LOG_DEBUG(Service_FS, "called. directory={}", name);
349 344
350 IPC::ResponseBuilder rb{ctx, 2}; 345 IPC::ResponseBuilder rb{ctx, 2};
351 rb.Push(backend.CreateDirectory(name)); 346 rb.Push(backend.CreateDirectory(name));
352 } 347 }
353 348
354 void DeleteDirectory(Kernel::HLERequestContext& ctx) { 349 void DeleteDirectory(Kernel::HLERequestContext& ctx) {
355 const IPC::RequestParser rp{ctx};
356
357 const auto file_buffer = ctx.ReadBuffer(); 350 const auto file_buffer = ctx.ReadBuffer();
358 std::string name = Common::StringFromBuffer(file_buffer); 351 const std::string name = Common::StringFromBuffer(file_buffer);
359 352
360 LOG_DEBUG(Service_FS, "called directory {}", name); 353 LOG_DEBUG(Service_FS, "called. directory={}", name);
361 354
362 IPC::ResponseBuilder rb{ctx, 2}; 355 IPC::ResponseBuilder rb{ctx, 2};
363 rb.Push(backend.DeleteDirectory(name)); 356 rb.Push(backend.DeleteDirectory(name));
364 } 357 }
365 358
366 void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) { 359 void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
367 const IPC::RequestParser rp{ctx};
368
369 const auto file_buffer = ctx.ReadBuffer(); 360 const auto file_buffer = ctx.ReadBuffer();
370 std::string name = Common::StringFromBuffer(file_buffer); 361 const std::string name = Common::StringFromBuffer(file_buffer);
371 362
372 LOG_DEBUG(Service_FS, "called directory {}", name); 363 LOG_DEBUG(Service_FS, "called. directory={}", name);
373 364
374 IPC::ResponseBuilder rb{ctx, 2}; 365 IPC::ResponseBuilder rb{ctx, 2};
375 rb.Push(backend.DeleteDirectoryRecursively(name)); 366 rb.Push(backend.DeleteDirectoryRecursively(name));
@@ -386,18 +377,16 @@ public:
386 } 377 }
387 378
388 void RenameFile(Kernel::HLERequestContext& ctx) { 379 void RenameFile(Kernel::HLERequestContext& ctx) {
389 IPC::RequestParser rp{ctx};
390
391 std::vector<u8> buffer; 380 std::vector<u8> buffer;
392 buffer.resize(ctx.BufferDescriptorX()[0].Size()); 381 buffer.resize(ctx.BufferDescriptorX()[0].Size());
393 Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size()); 382 Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
394 std::string src_name = Common::StringFromBuffer(buffer); 383 const std::string src_name = Common::StringFromBuffer(buffer);
395 384
396 buffer.resize(ctx.BufferDescriptorX()[1].Size()); 385 buffer.resize(ctx.BufferDescriptorX()[1].Size());
397 Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size()); 386 Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
398 std::string dst_name = Common::StringFromBuffer(buffer); 387 const std::string dst_name = Common::StringFromBuffer(buffer);
399 388
400 LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); 389 LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
401 390
402 IPC::ResponseBuilder rb{ctx, 2}; 391 IPC::ResponseBuilder rb{ctx, 2};
403 rb.Push(backend.RenameFile(src_name, dst_name)); 392 rb.Push(backend.RenameFile(src_name, dst_name));
@@ -406,12 +395,12 @@ public:
406 void OpenFile(Kernel::HLERequestContext& ctx) { 395 void OpenFile(Kernel::HLERequestContext& ctx) {
407 IPC::RequestParser rp{ctx}; 396 IPC::RequestParser rp{ctx};
408 397
409 auto file_buffer = ctx.ReadBuffer(); 398 const auto file_buffer = ctx.ReadBuffer();
410 std::string name = Common::StringFromBuffer(file_buffer); 399 const std::string name = Common::StringFromBuffer(file_buffer);
411 400
412 auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); 401 const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
413 402
414 LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); 403 LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, static_cast<u32>(mode));
415 404
416 auto result = backend.OpenFile(name, mode); 405 auto result = backend.OpenFile(name, mode);
417 if (result.Failed()) { 406 if (result.Failed()) {
@@ -430,13 +419,13 @@ public:
430 void OpenDirectory(Kernel::HLERequestContext& ctx) { 419 void OpenDirectory(Kernel::HLERequestContext& ctx) {
431 IPC::RequestParser rp{ctx}; 420 IPC::RequestParser rp{ctx};
432 421
433 auto file_buffer = ctx.ReadBuffer(); 422 const auto file_buffer = ctx.ReadBuffer();
434 std::string name = Common::StringFromBuffer(file_buffer); 423 const std::string name = Common::StringFromBuffer(file_buffer);
435 424
436 // TODO(Subv): Implement this filter. 425 // TODO(Subv): Implement this filter.
437 u32 filter_flags = rp.Pop<u32>(); 426 const u32 filter_flags = rp.Pop<u32>();
438 427
439 LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); 428 LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags);
440 429
441 auto result = backend.OpenDirectory(name); 430 auto result = backend.OpenDirectory(name);
442 if (result.Failed()) { 431 if (result.Failed()) {
@@ -453,12 +442,10 @@ public:
453 } 442 }
454 443
455 void GetEntryType(Kernel::HLERequestContext& ctx) { 444 void GetEntryType(Kernel::HLERequestContext& ctx) {
456 IPC::RequestParser rp{ctx}; 445 const auto file_buffer = ctx.ReadBuffer();
457 446 const std::string name = Common::StringFromBuffer(file_buffer);
458 auto file_buffer = ctx.ReadBuffer();
459 std::string name = Common::StringFromBuffer(file_buffer);
460 447
461 LOG_DEBUG(Service_FS, "called file {}", name); 448 LOG_DEBUG(Service_FS, "called. file={}", name);
462 449
463 auto result = backend.GetEntryType(name); 450 auto result = backend.GetEntryType(name);
464 if (result.Failed()) { 451 if (result.Failed()) {
@@ -616,7 +603,9 @@ private:
616 u64_le save_id; 603 u64_le save_id;
617 u64_le title_id; 604 u64_le title_id;
618 u64_le save_image_size; 605 u64_le save_image_size;
619 INSERT_PADDING_BYTES(0x28); 606 u16_le index;
607 FileSys::SaveDataRank rank;
608 INSERT_PADDING_BYTES(0x25);
620 }; 609 };
621 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); 610 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
622 611
@@ -675,10 +664,13 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
675 {100, nullptr, "OpenImageDirectoryFileSystem"}, 664 {100, nullptr, "OpenImageDirectoryFileSystem"},
676 {110, nullptr, "OpenContentStorageFileSystem"}, 665 {110, nullptr, "OpenContentStorageFileSystem"},
677 {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, 666 {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
667 {130, nullptr, "OpenCustomStorageFileSystem"},
678 {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, 668 {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
679 {201, nullptr, "OpenDataStorageByProgramId"}, 669 {201, nullptr, "OpenDataStorageByProgramId"},
680 {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"}, 670 {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"},
681 {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"}, 671 {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"},
672 {204, nullptr, "OpenDataFileSystemByProgramIndex"},
673 {205, nullptr, "OpenDataStorageByProgramIndex"},
682 {400, nullptr, "OpenDeviceOperator"}, 674 {400, nullptr, "OpenDeviceOperator"},
683 {500, nullptr, "OpenSdCardDetectionEventNotifier"}, 675 {500, nullptr, "OpenSdCardDetectionEventNotifier"},
684 {501, nullptr, "OpenGameCardDetectionEventNotifier"}, 676 {501, nullptr, "OpenGameCardDetectionEventNotifier"},
@@ -702,6 +694,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
702 {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"}, 694 {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"},
703 {615, nullptr, "QuerySaveDataInternalStorageTotalSize"}, 695 {615, nullptr, "QuerySaveDataInternalStorageTotalSize"},
704 {616, nullptr, "GetSaveDataCommitId"}, 696 {616, nullptr, "GetSaveDataCommitId"},
697 {617, nullptr, "UnregisterExternalKey"},
705 {620, nullptr, "SetSdCardEncryptionSeed"}, 698 {620, nullptr, "SetSdCardEncryptionSeed"},
706 {630, nullptr, "SetSdCardAccessibility"}, 699 {630, nullptr, "SetSdCardAccessibility"},
707 {631, nullptr, "IsSdCardAccessible"}, 700 {631, nullptr, "IsSdCardAccessible"},
@@ -712,6 +705,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
712 {710, nullptr, "ResolveAccessFailure"}, 705 {710, nullptr, "ResolveAccessFailure"},
713 {720, nullptr, "AbandonAccessFailure"}, 706 {720, nullptr, "AbandonAccessFailure"},
714 {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"}, 707 {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
708 {810, nullptr, "RegisterProgramIndexMapInfo"},
715 {1000, nullptr, "SetBisRootForHost"}, 709 {1000, nullptr, "SetBisRootForHost"},
716 {1001, nullptr, "SetSaveDataSize"}, 710 {1001, nullptr, "SetSaveDataSize"},
717 {1002, nullptr, "SetSaveDataRootPath"}, 711 {1002, nullptr, "SetSaveDataRootPath"},
@@ -722,6 +716,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
722 {1007, nullptr, "RegisterUpdatePartition"}, 716 {1007, nullptr, "RegisterUpdatePartition"},
723 {1008, nullptr, "OpenRegisteredUpdatePartition"}, 717 {1008, nullptr, "OpenRegisteredUpdatePartition"},
724 {1009, nullptr, "GetAndClearMemoryReportInfo"}, 718 {1009, nullptr, "GetAndClearMemoryReportInfo"},
719 {1010, nullptr, "SetDataStorageRedirectTarget"},
720 {1011, nullptr, "OutputAccessLogToSdCard2"},
725 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, 721 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
726 {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, 722 {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
727 {1200, nullptr, "OpenMultiCommitManager"}, 723 {1200, nullptr, "OpenMultiCommitManager"},
@@ -779,16 +775,17 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
779} 775}
780 776
781void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { 777void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
782 IPC::RequestParser rp{ctx}; 778 LOG_INFO(Service_FS, "called.");
783 779
784 auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>(); 780 struct Parameters {
785 auto unk = rp.Pop<u32>(); 781 FileSys::SaveDataSpaceId save_data_space_id;
786 LOG_INFO(Service_FS, "called with unknown={:08X}", unk); 782 FileSys::SaveDataDescriptor descriptor;
787 783 };
788 auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
789 784
790 auto dir = OpenSaveData(space_id, save_struct); 785 IPC::RequestParser rp{ctx};
786 const auto parameters = rp.PopRaw<Parameters>();
791 787
788 auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
792 if (dir.Failed()) { 789 if (dir.Failed()) {
793 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 790 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
794 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); 791 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index d9225d624..5100e376c 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -12,6 +12,7 @@ namespace Service::Friend {
12class IFriendService final : public ServiceFramework<IFriendService> { 12class IFriendService final : public ServiceFramework<IFriendService> {
13public: 13public:
14 IFriendService() : ServiceFramework("IFriendService") { 14 IFriendService() : ServiceFramework("IFriendService") {
15 // clang-format off
15 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
16 {0, nullptr, "GetCompletionEvent"}, 17 {0, nullptr, "GetCompletionEvent"},
17 {1, nullptr, "Cancel"}, 18 {1, nullptr, "Cancel"},
@@ -24,8 +25,7 @@ public:
24 {10400, nullptr, "GetBlockedUserListIds"}, 25 {10400, nullptr, "GetBlockedUserListIds"},
25 {10500, nullptr, "GetProfileList"}, 26 {10500, nullptr, "GetProfileList"},
26 {10600, nullptr, "DeclareOpenOnlinePlaySession"}, 27 {10600, nullptr, "DeclareOpenOnlinePlaySession"},
27 {10601, &IFriendService::DeclareCloseOnlinePlaySession, 28 {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"},
28 "DeclareCloseOnlinePlaySession"},
29 {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"}, 29 {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"},
30 {10700, nullptr, "GetPlayHistoryRegistrationKey"}, 30 {10700, nullptr, "GetPlayHistoryRegistrationKey"},
31 {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"}, 31 {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"},
@@ -88,6 +88,7 @@ public:
88 {30830, nullptr, "ClearPlayLog"}, 88 {30830, nullptr, "ClearPlayLog"},
89 {49900, nullptr, "DeleteNetworkServiceAccountCache"}, 89 {49900, nullptr, "DeleteNetworkServiceAccountCache"},
90 }; 90 };
91 // clang-format on
91 92
92 RegisterHandlers(functions); 93 RegisterHandlers(functions);
93 } 94 }
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 63b55758b..a4ad95d96 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -210,6 +210,7 @@ Hid::Hid() : ServiceFramework("hid") {
210 {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"}, 210 {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
211 {132, nullptr, "EnableUnintendedHomeButtonInputProtection"}, 211 {132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
212 {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, 212 {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
213 {134, nullptr, "SetNpadAnalogStickUseCenterClamp"},
213 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, 214 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
214 {201, &Hid::SendVibrationValue, "SendVibrationValue"}, 215 {201, &Hid::SendVibrationValue, "SendVibrationValue"},
215 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, 216 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
@@ -221,6 +222,7 @@ Hid::Hid() : ServiceFramework("hid") {
221 {208, nullptr, "GetActualVibrationGcErmCommand"}, 222 {208, nullptr, "GetActualVibrationGcErmCommand"},
222 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, 223 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
223 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, 224 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
225 {211, nullptr, "IsVibrationDeviceMounted"},
224 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, 226 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
225 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, 227 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
226 {302, nullptr, "StopConsoleSixAxisSensor"}, 228 {302, nullptr, "StopConsoleSixAxisSensor"},
@@ -265,6 +267,7 @@ Hid::Hid() : ServiceFramework("hid") {
265 {523, nullptr, "SetIsPalmaPairedConnectable"}, 267 {523, nullptr, "SetIsPalmaPairedConnectable"},
266 {524, nullptr, "PairPalma"}, 268 {524, nullptr, "PairPalma"},
267 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, 269 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
270 {526, nullptr, "CancelWritePalmaWaveEntry"},
268 {1000, nullptr, "SetNpadCommunicationMode"}, 271 {1000, nullptr, "SetNpadCommunicationMode"},
269 {1001, nullptr, "GetNpadCommunicationMode"}, 272 {1001, nullptr, "GetNpadCommunicationMode"},
270 }; 273 };
@@ -797,12 +800,22 @@ public:
797 {232, nullptr, "EnableShipmentMode"}, 800 {232, nullptr, "EnableShipmentMode"},
798 {233, nullptr, "ClearPairingInfo"}, 801 {233, nullptr, "ClearPairingInfo"},
799 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, 802 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
803 {235, nullptr, "EnableAnalogStickPower"},
800 {301, nullptr, "GetAbstractedPadHandles"}, 804 {301, nullptr, "GetAbstractedPadHandles"},
801 {302, nullptr, "GetAbstractedPadState"}, 805 {302, nullptr, "GetAbstractedPadState"},
802 {303, nullptr, "GetAbstractedPadsState"}, 806 {303, nullptr, "GetAbstractedPadsState"},
803 {321, nullptr, "SetAutoPilotVirtualPadState"}, 807 {321, nullptr, "SetAutoPilotVirtualPadState"},
804 {322, nullptr, "UnsetAutoPilotVirtualPadState"}, 808 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
805 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, 809 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
810 {324, nullptr, "AttachHdlsWorkBuffer"},
811 {325, nullptr, "ReleaseHdlsWorkBuffer"},
812 {326, nullptr, "DumpHdlsNpadAssignmentState"},
813 {327, nullptr, "DumpHdlsStates"},
814 {328, nullptr, "ApplyHdlsNpadAssignmentState"},
815 {329, nullptr, "ApplyHdlsStateList"},
816 {330, nullptr, "AttachHdlsVirtualDevice"},
817 {331, nullptr, "DetachHdlsVirtualDevice"},
818 {332, nullptr, "SetHdlsState"},
806 {350, nullptr, "AddRegisteredDevice"}, 819 {350, nullptr, "AddRegisteredDevice"},
807 {400, nullptr, "DisableExternalMcuOnNxDevice"}, 820 {400, nullptr, "DisableExternalMcuOnNxDevice"},
808 {401, nullptr, "DisableRailDeviceFiltering"}, 821 {401, nullptr, "DisableRailDeviceFiltering"},
@@ -825,6 +838,7 @@ public:
825 {131, nullptr, "ActivateSleepButton"}, 838 {131, nullptr, "ActivateSleepButton"},
826 {141, nullptr, "AcquireCaptureButtonEventHandle"}, 839 {141, nullptr, "AcquireCaptureButtonEventHandle"},
827 {151, nullptr, "ActivateCaptureButton"}, 840 {151, nullptr, "ActivateCaptureButton"},
841 {161, nullptr, "GetPlatformConfig"},
828 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, 842 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
829 {211, nullptr, "GetNpadsWithNfc"}, 843 {211, nullptr, "GetNpadsWithNfc"},
830 {212, nullptr, "AcquireNfcActivateEventHandle"}, 844 {212, nullptr, "AcquireNfcActivateEventHandle"},
@@ -894,6 +908,7 @@ public:
894 {827, nullptr, "IsAnalogStickButtonPressed"}, 908 {827, nullptr, "IsAnalogStickButtonPressed"},
895 {828, nullptr, "IsAnalogStickInReleasePosition"}, 909 {828, nullptr, "IsAnalogStickInReleasePosition"},
896 {829, nullptr, "IsAnalogStickInCircumference"}, 910 {829, nullptr, "IsAnalogStickInCircumference"},
911 {830, nullptr, "SetNotificationLedPattern"},
897 {850, nullptr, "IsUsbFullKeyControllerEnabled"}, 912 {850, nullptr, "IsUsbFullKeyControllerEnabled"},
898 {851, nullptr, "EnableUsbFullKeyController"}, 913 {851, nullptr, "EnableUsbFullKeyController"},
899 {852, nullptr, "IsUsbConnected"}, 914 {852, nullptr, "IsUsbConnected"},
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index e250595e3..ed5059047 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -52,9 +52,11 @@ public:
52 } 52 }
53}; 53};
54 54
55class ILocalCommunicationService final : public ServiceFramework<ILocalCommunicationService> { 55class ISystemLocalCommunicationService final
56 : public ServiceFramework<ISystemLocalCommunicationService> {
56public: 57public:
57 explicit ILocalCommunicationService(const char* name) : ServiceFramework{name} { 58 explicit ISystemLocalCommunicationService()
59 : ServiceFramework{"ISystemLocalCommunicationService"} {
58 // clang-format off 60 // clang-format off
59 static const FunctionInfo functions[] = { 61 static const FunctionInfo functions[] = {
60 {0, nullptr, "GetState"}, 62 {0, nullptr, "GetState"},
@@ -84,6 +86,50 @@ public:
84 {304, nullptr, "Disconnect"}, 86 {304, nullptr, "Disconnect"},
85 {400, nullptr, "InitializeSystem"}, 87 {400, nullptr, "InitializeSystem"},
86 {401, nullptr, "FinalizeSystem"}, 88 {401, nullptr, "FinalizeSystem"},
89 {402, nullptr, "SetOperationMode"},
90 {403, nullptr, "InitializeSystem2"},
91 };
92 // clang-format on
93
94 RegisterHandlers(functions);
95 }
96};
97
98class IUserLocalCommunicationService final
99 : public ServiceFramework<IUserLocalCommunicationService> {
100public:
101 explicit IUserLocalCommunicationService() : ServiceFramework{"IUserLocalCommunicationService"} {
102 // clang-format off
103 static const FunctionInfo functions[] = {
104 {0, nullptr, "GetState"},
105 {1, nullptr, "GetNetworkInfo"},
106 {2, nullptr, "GetIpv4Address"},
107 {3, nullptr, "GetDisconnectReason"},
108 {4, nullptr, "GetSecurityParameter"},
109 {5, nullptr, "GetNetworkConfig"},
110 {100, nullptr, "AttachStateChangeEvent"},
111 {101, nullptr, "GetNetworkInfoLatestUpdate"},
112 {102, nullptr, "Scan"},
113 {103, nullptr, "ScanPrivate"},
114 {104, nullptr, "SetWirelessControllerRestriction"},
115 {200, nullptr, "OpenAccessPoint"},
116 {201, nullptr, "CloseAccessPoint"},
117 {202, nullptr, "CreateNetwork"},
118 {203, nullptr, "CreateNetworkPrivate"},
119 {204, nullptr, "DestroyNetwork"},
120 {205, nullptr, "Reject"},
121 {206, nullptr, "SetAdvertiseData"},
122 {207, nullptr, "SetStationAcceptPolicy"},
123 {208, nullptr, "AddAcceptFilterEntry"},
124 {209, nullptr, "ClearAcceptFilter"},
125 {300, nullptr, "OpenStation"},
126 {301, nullptr, "CloseStation"},
127 {302, nullptr, "Connect"},
128 {303, nullptr, "ConnectPrivate"},
129 {304, nullptr, "Disconnect"},
130 {400, nullptr, "Initialize"},
131 {401, nullptr, "Finalize"},
132 {402, nullptr, "SetOperationMode"},
87 }; 133 };
88 // clang-format on 134 // clang-format on
89 135
@@ -108,7 +154,7 @@ public:
108 154
109 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 155 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
110 rb.Push(RESULT_SUCCESS); 156 rb.Push(RESULT_SUCCESS);
111 rb.PushIpcInterface<ILocalCommunicationService>("ISystemLocalCommunicationService"); 157 rb.PushIpcInterface<ISystemLocalCommunicationService>();
112 } 158 }
113}; 159};
114 160
@@ -129,7 +175,7 @@ public:
129 175
130 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 176 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
131 rb.Push(RESULT_SUCCESS); 177 rb.Push(RESULT_SUCCESS);
132 rb.PushIpcInterface<ILocalCommunicationService>("IUserLocalCommunicationService"); 178 rb.PushIpcInterface<IUserLocalCommunicationService>();
133 } 179 }
134}; 180};
135 181
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index d65693fc7..5af925515 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -86,6 +86,7 @@ public:
86 {2, &RelocatableObject::LoadNrr, "LoadNrr"}, 86 {2, &RelocatableObject::LoadNrr, "LoadNrr"},
87 {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, 87 {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
88 {4, &RelocatableObject::Initialize, "Initialize"}, 88 {4, &RelocatableObject::Initialize, "Initialize"},
89 {10, nullptr, "LoadNrrEx"},
89 }; 90 };
90 // clang-format on 91 // clang-format on
91 92
@@ -93,12 +94,18 @@ public:
93 } 94 }
94 95
95 void LoadNrr(Kernel::HLERequestContext& ctx) { 96 void LoadNrr(Kernel::HLERequestContext& ctx) {
97 struct Parameters {
98 u64_le process_id;
99 u64_le nrr_address;
100 u64_le nrr_size;
101 };
102
96 IPC::RequestParser rp{ctx}; 103 IPC::RequestParser rp{ctx};
97 rp.Skip(2, false); 104 const auto [process_id, nrr_address, nrr_size] = rp.PopRaw<Parameters>();
98 const VAddr nrr_addr{rp.Pop<VAddr>()}; 105
99 const u64 nrr_size{rp.Pop<u64>()}; 106 LOG_DEBUG(Service_LDR,
100 LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}, nrr_size={:016X}", nrr_addr, 107 "called with process_id={:016X}, nrr_address={:016X}, nrr_size={:016X}",
101 nrr_size); 108 process_id, nrr_address, nrr_size);
102 109
103 if (!initialized) { 110 if (!initialized) {
104 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); 111 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
@@ -116,24 +123,26 @@ public:
116 } 123 }
117 124
118 // NRR Address does not fall on 0x1000 byte boundary 125 // NRR Address does not fall on 0x1000 byte boundary
119 if (!Common::Is4KBAligned(nrr_addr)) { 126 if (!Common::Is4KBAligned(nrr_address)) {
120 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); 127 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
128 nrr_address);
121 IPC::ResponseBuilder rb{ctx, 2}; 129 IPC::ResponseBuilder rb{ctx, 2};
122 rb.Push(ERROR_INVALID_ALIGNMENT); 130 rb.Push(ERROR_INVALID_ALIGNMENT);
123 return; 131 return;
124 } 132 }
125 133
126 // NRR Size is zero or causes overflow 134 // NRR Size is zero or causes overflow
127 if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) { 135 if (nrr_address + nrr_size <= nrr_address || nrr_size == 0 ||
136 !Common::Is4KBAligned(nrr_size)) {
128 LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", 137 LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
129 nrr_addr, nrr_size); 138 nrr_address, nrr_size);
130 IPC::ResponseBuilder rb{ctx, 2}; 139 IPC::ResponseBuilder rb{ctx, 2};
131 rb.Push(ERROR_INVALID_SIZE); 140 rb.Push(ERROR_INVALID_SIZE);
132 return; 141 return;
133 } 142 }
134 // Read NRR data from memory 143 // Read NRR data from memory
135 std::vector<u8> nrr_data(nrr_size); 144 std::vector<u8> nrr_data(nrr_size);
136 Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size); 145 Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size);
137 NRRHeader header; 146 NRRHeader header;
138 std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); 147 std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
139 148
@@ -174,7 +183,7 @@ public:
174 hashes.emplace_back(hash); 183 hashes.emplace_back(hash);
175 } 184 }
176 185
177 nrr.insert_or_assign(nrr_addr, std::move(hashes)); 186 nrr.insert_or_assign(nrr_address, std::move(hashes));
178 187
179 IPC::ResponseBuilder rb{ctx, 2}; 188 IPC::ResponseBuilder rb{ctx, 2};
180 rb.Push(RESULT_SUCCESS); 189 rb.Push(RESULT_SUCCESS);
@@ -188,23 +197,30 @@ public:
188 return; 197 return;
189 } 198 }
190 199
200 struct Parameters {
201 u64_le process_id;
202 u64_le nrr_address;
203 };
204
191 IPC::RequestParser rp{ctx}; 205 IPC::RequestParser rp{ctx};
192 rp.Skip(2, false); 206 const auto [process_id, nrr_address] = rp.PopRaw<Parameters>();
193 const auto nrr_addr{rp.Pop<VAddr>()}; 207
194 LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}", nrr_addr); 208 LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id,
209 nrr_address);
195 210
196 if (!Common::Is4KBAligned(nrr_addr)) { 211 if (!Common::Is4KBAligned(nrr_address)) {
197 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); 212 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
213 nrr_address);
198 IPC::ResponseBuilder rb{ctx, 2}; 214 IPC::ResponseBuilder rb{ctx, 2};
199 rb.Push(ERROR_INVALID_ALIGNMENT); 215 rb.Push(ERROR_INVALID_ALIGNMENT);
200 return; 216 return;
201 } 217 }
202 218
203 const auto iter = nrr.find(nrr_addr); 219 const auto iter = nrr.find(nrr_address);
204 if (iter == nrr.end()) { 220 if (iter == nrr.end()) {
205 LOG_ERROR(Service_LDR, 221 LOG_ERROR(Service_LDR,
206 "Attempting to unload NRR which has not been loaded! (addr={:016X})", 222 "Attempting to unload NRR which has not been loaded! (addr={:016X})",
207 nrr_addr); 223 nrr_address);
208 IPC::ResponseBuilder rb{ctx, 2}; 224 IPC::ResponseBuilder rb{ctx, 2};
209 rb.Push(ERROR_INVALID_NRR_ADDRESS); 225 rb.Push(ERROR_INVALID_NRR_ADDRESS);
210 return; 226 return;
@@ -216,16 +232,22 @@ public:
216 } 232 }
217 233
218 void LoadNro(Kernel::HLERequestContext& ctx) { 234 void LoadNro(Kernel::HLERequestContext& ctx) {
235 struct Parameters {
236 u64_le process_id;
237 u64_le image_address;
238 u64_le image_size;
239 u64_le bss_address;
240 u64_le bss_size;
241 };
242
219 IPC::RequestParser rp{ctx}; 243 IPC::RequestParser rp{ctx};
220 rp.Skip(2, false); 244 const auto [process_id, nro_address, nro_size, bss_address, bss_size] =
221 const VAddr nro_addr{rp.Pop<VAddr>()}; 245 rp.PopRaw<Parameters>();
222 const u64 nro_size{rp.Pop<u64>()}; 246
223 const VAddr bss_addr{rp.Pop<VAddr>()}; 247 LOG_DEBUG(Service_LDR,
224 const u64 bss_size{rp.Pop<u64>()}; 248 "called with pid={:016X}, nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, "
225 LOG_DEBUG( 249 "bss_size={:016X}",
226 Service_LDR, 250 process_id, nro_address, nro_size, bss_address, bss_size);
227 "called with nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, bss_size={:016X}",
228 nro_addr, nro_size, bss_addr, bss_size);
229 251
230 if (!initialized) { 252 if (!initialized) {
231 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); 253 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
@@ -243,8 +265,9 @@ public:
243 } 265 }
244 266
245 // NRO Address does not fall on 0x1000 byte boundary 267 // NRO Address does not fall on 0x1000 byte boundary
246 if (!Common::Is4KBAligned(nro_addr)) { 268 if (!Common::Is4KBAligned(nro_address)) {
247 LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr); 269 LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!",
270 nro_address);
248 IPC::ResponseBuilder rb{ctx, 2}; 271 IPC::ResponseBuilder rb{ctx, 2};
249 rb.Push(ERROR_INVALID_ALIGNMENT); 272 rb.Push(ERROR_INVALID_ALIGNMENT);
250 return; 273 return;
@@ -252,15 +275,15 @@ public:
252 275
253 // NRO Size or BSS Size is zero or causes overflow 276 // NRO Size or BSS Size is zero or causes overflow
254 const auto nro_size_valid = 277 const auto nro_size_valid =
255 nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size); 278 nro_size != 0 && nro_address + nro_size > nro_address && Common::Is4KBAligned(nro_size);
256 const auto bss_size_valid = 279 const auto bss_size_valid = nro_size + bss_size >= nro_size &&
257 nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr); 280 (bss_size == 0 || bss_address + bss_size > bss_address);
258 281
259 if (!nro_size_valid || !bss_size_valid) { 282 if (!nro_size_valid || !bss_size_valid) {
260 LOG_ERROR(Service_LDR, 283 LOG_ERROR(Service_LDR,
261 "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " 284 "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
262 "bss_address={:016X}, bss_size={:016X})", 285 "bss_address={:016X}, bss_size={:016X})",
263 nro_addr, nro_size, bss_addr, bss_size); 286 nro_address, nro_size, bss_address, bss_size);
264 IPC::ResponseBuilder rb{ctx, 2}; 287 IPC::ResponseBuilder rb{ctx, 2};
265 rb.Push(ERROR_INVALID_SIZE); 288 rb.Push(ERROR_INVALID_SIZE);
266 return; 289 return;
@@ -268,7 +291,7 @@ public:
268 291
269 // Read NRO data from memory 292 // Read NRO data from memory
270 std::vector<u8> nro_data(nro_size); 293 std::vector<u8> nro_data(nro_size);
271 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); 294 Memory::ReadBlock(nro_address, nro_data.data(), nro_size);
272 295
273 SHA256Hash hash{}; 296 SHA256Hash hash{};
274 mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); 297 mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
@@ -318,17 +341,18 @@ public:
318 return; 341 return;
319 } 342 }
320 343
321 ASSERT(vm_manager 344 ASSERT(
322 .MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode) 345 vm_manager
323 .IsSuccess()); 346 .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode)
324 ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); 347 .IsSuccess());
348 ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess());
325 349
326 if (bss_size > 0) { 350 if (bss_size > 0) {
327 ASSERT(vm_manager 351 ASSERT(vm_manager
328 .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, 352 .MirrorMemory(*map_address + nro_size, bss_address, bss_size,
329 Kernel::MemoryState::ModuleCode) 353 Kernel::MemoryState::ModuleCode)
330 .IsSuccess()); 354 .IsSuccess());
331 ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); 355 ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess());
332 } 356 }
333 357
334 vm_manager.ReprotectRange(*map_address, header.text_size, 358 vm_manager.ReprotectRange(*map_address, header.text_size,
@@ -348,13 +372,6 @@ public:
348 } 372 }
349 373
350 void UnloadNro(Kernel::HLERequestContext& ctx) { 374 void UnloadNro(Kernel::HLERequestContext& ctx) {
351 IPC::RequestParser rp{ctx};
352 rp.Skip(2, false);
353 const VAddr mapped_addr{rp.PopRaw<VAddr>()};
354 const VAddr heap_addr{rp.PopRaw<VAddr>()};
355 LOG_DEBUG(Service_LDR, "called with mapped_addr={:016X}, heap_addr={:016X}", mapped_addr,
356 heap_addr);
357
358 if (!initialized) { 375 if (!initialized) {
359 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); 376 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
360 IPC::ResponseBuilder rb{ctx, 2}; 377 IPC::ResponseBuilder rb{ctx, 2};
@@ -362,22 +379,30 @@ public:
362 return; 379 return;
363 } 380 }
364 381
365 if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) { 382 struct Parameters {
366 LOG_ERROR(Service_LDR, 383 u64_le process_id;
367 "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, " 384 u64_le nro_address;
368 "bss_addr={:016X})!", 385 };
369 mapped_addr, heap_addr); 386
387 IPC::RequestParser rp{ctx};
388 const auto [process_id, nro_address] = rp.PopRaw<Parameters>();
389 LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nro_address=0x{:016X}", process_id,
390 nro_address);
391
392 if (!Common::Is4KBAligned(nro_address)) {
393 LOG_ERROR(Service_LDR, "NRO address has invalid alignment (nro_address=0x{:016X})",
394 nro_address);
370 IPC::ResponseBuilder rb{ctx, 2}; 395 IPC::ResponseBuilder rb{ctx, 2};
371 rb.Push(ERROR_INVALID_ALIGNMENT); 396 rb.Push(ERROR_INVALID_ALIGNMENT);
372 return; 397 return;
373 } 398 }
374 399
375 const auto iter = nro.find(mapped_addr); 400 const auto iter = nro.find(nro_address);
376 if (iter == nro.end()) { 401 if (iter == nro.end()) {
377 LOG_ERROR(Service_LDR, 402 LOG_ERROR(Service_LDR,
378 "The NRO attempting to unmap was not mapped or has an invalid address " 403 "The NRO attempting to be unmapped was not mapped or has an invalid address "
379 "(actual {:016X})!", 404 "(nro_address=0x{:016X})!",
380 mapped_addr); 405 nro_address);
381 IPC::ResponseBuilder rb{ctx, 2}; 406 IPC::ResponseBuilder rb{ctx, 2};
382 rb.Push(ERROR_INVALID_NRO_ADDRESS); 407 rb.Push(ERROR_INVALID_NRO_ADDRESS);
383 return; 408 return;
@@ -386,10 +411,7 @@ public:
386 auto& vm_manager = Core::CurrentProcess()->VMManager(); 411 auto& vm_manager = Core::CurrentProcess()->VMManager();
387 const auto& nro_size = iter->second.size; 412 const auto& nro_size = iter->second.size;
388 413
389 ASSERT(vm_manager 414 ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess());
390 .MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode)
391 .IsSuccess());
392 ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
393 415
394 Core::System::GetInstance().InvalidateCpuInstructionCaches(); 416 Core::System::GetInstance().InvalidateCpuInstructionCaches();
395 417
@@ -459,11 +481,10 @@ private:
459 std::map<VAddr, NROInfo> nro; 481 std::map<VAddr, NROInfo> nro;
460 std::map<VAddr, std::vector<SHA256Hash>> nrr; 482 std::map<VAddr, std::vector<SHA256Hash>> nrr;
461 483
462 bool IsValidNROHash(const SHA256Hash& hash) { 484 bool IsValidNROHash(const SHA256Hash& hash) const {
463 return std::any_of( 485 return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
464 nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) { 486 return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
465 return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); 487 });
466 });
467 } 488 }
468 489
469 static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { 490 static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 60479bb45..f92571008 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -15,12 +15,16 @@ namespace Service::NIFM {
15class IScanRequest final : public ServiceFramework<IScanRequest> { 15class IScanRequest final : public ServiceFramework<IScanRequest> {
16public: 16public:
17 explicit IScanRequest() : ServiceFramework("IScanRequest") { 17 explicit IScanRequest() : ServiceFramework("IScanRequest") {
18 // clang-format off
18 static const FunctionInfo functions[] = { 19 static const FunctionInfo functions[] = {
19 {0, nullptr, "Submit"}, 20 {0, nullptr, "Submit"},
20 {1, nullptr, "IsProcessing"}, 21 {1, nullptr, "IsProcessing"},
21 {2, nullptr, "GetResult"}, 22 {2, nullptr, "GetResult"},
22 {3, nullptr, "GetSystemEventReadableHandle"}, 23 {3, nullptr, "GetSystemEventReadableHandle"},
24 {4, nullptr, "SetChannels"},
23 }; 25 };
26 // clang-format on
27
24 RegisterHandlers(functions); 28 RegisterHandlers(functions);
25 } 29 }
26}; 30};
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp
index ccb6f9da9..8751522ca 100644
--- a/src/core/hle/service/npns/npns.cpp
+++ b/src/core/hle/service/npns/npns.cpp
@@ -45,7 +45,7 @@ public:
45 {114, nullptr, "AttachJid"}, 45 {114, nullptr, "AttachJid"},
46 {115, nullptr, "DetachJid"}, 46 {115, nullptr, "DetachJid"},
47 {201, nullptr, "RequestChangeStateForceTimed"}, 47 {201, nullptr, "RequestChangeStateForceTimed"},
48 {102, nullptr, "RequestChangeStateForceAsync"}, 48 {202, nullptr, "RequestChangeStateForceAsync"},
49 }; 49 };
50 // clang-format on 50 // clang-format on
51 51
@@ -73,6 +73,7 @@ public:
73 {103, nullptr, "GetState"}, 73 {103, nullptr, "GetState"},
74 {104, nullptr, "GetStatistics"}, 74 {104, nullptr, "GetStatistics"},
75 {111, nullptr, "GetJid"}, 75 {111, nullptr, "GetJid"},
76 {120, nullptr, "CreateNotificationReceiver"},
76 }; 77 };
77 // clang-format on 78 // clang-format on
78 79
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index ace71169f..12f3ef825 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -18,7 +18,7 @@ class nvmap;
18class nvdisp_disp0 final : public nvdevice { 18class nvdisp_disp0 final : public nvdevice {
19public: 19public:
20 explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev); 20 explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev);
21 ~nvdisp_disp0(); 21 ~nvdisp_disp0() override;
22 22
23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
24 24
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index fe311b069..5b4889910 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -17,7 +17,7 @@ namespace Service::Nvidia {
17class NVDRV final : public ServiceFramework<NVDRV> { 17class NVDRV final : public ServiceFramework<NVDRV> {
18public: 18public:
19 NVDRV(std::shared_ptr<Module> nvdrv, const char* name); 19 NVDRV(std::shared_ptr<Module> nvdrv, const char* name);
20 ~NVDRV(); 20 ~NVDRV() override;
21 21
22private: 22private:
23 void Open(Kernel::HLERequestContext& ctx); 23 void Open(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h
index 5a4dfc1f9..6eafb1346 100644
--- a/src/core/hle/service/nvdrv/nvmemp.h
+++ b/src/core/hle/service/nvdrv/nvmemp.h
@@ -11,7 +11,7 @@ namespace Service::Nvidia {
11class NVMEMP final : public ServiceFramework<NVMEMP> { 11class NVMEMP final : public ServiceFramework<NVMEMP> {
12public: 12public:
13 NVMEMP(); 13 NVMEMP();
14 ~NVMEMP(); 14 ~NVMEMP() override;
15 15
16private: 16private:
17 void Cmd0(Kernel::HLERequestContext& ctx); 17 void Cmd0(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index c7f5bbf28..3c5c53e24 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -21,12 +21,13 @@
21#include "core/hle/service/vi/display/vi_display.h" 21#include "core/hle/service/vi/display/vi_display.h"
22#include "core/hle/service/vi/layer/vi_layer.h" 22#include "core/hle/service/vi/layer/vi_layer.h"
23#include "core/perf_stats.h" 23#include "core/perf_stats.h"
24#include "core/settings.h"
24#include "video_core/renderer_base.h" 25#include "video_core/renderer_base.h"
25 26
26namespace Service::NVFlinger { 27namespace Service::NVFlinger {
27 28
28constexpr std::size_t SCREEN_REFRESH_RATE = 60; 29constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
29constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); 30constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30);
30 31
31NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { 32NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
32 displays.emplace_back(0, "Default"); 33 displays.emplace_back(0, "Default");
@@ -36,13 +37,15 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
36 displays.emplace_back(4, "Null"); 37 displays.emplace_back(4, "Null");
37 38
38 // Schedule the screen composition events 39 // Schedule the screen composition events
39 composition_event = 40 const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
40 core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { 41
42 composition_event = core_timing.RegisterEvent(
43 "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) {
41 Compose(); 44 Compose();
42 this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event); 45 this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event);
43 }); 46 });
44 47
45 core_timing.ScheduleEvent(frame_ticks, composition_event); 48 core_timing.ScheduleEvent(ticks, composition_event);
46} 49}
47 50
48NVFlinger::~NVFlinger() { 51NVFlinger::~NVFlinger() {
@@ -62,6 +65,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
62 const auto itr = 65 const auto itr =
63 std::find_if(displays.begin(), displays.end(), 66 std::find_if(displays.begin(), displays.end(),
64 [&](const VI::Display& display) { return display.GetName() == name; }); 67 [&](const VI::Display& display) { return display.GetName() == name; });
68
65 if (itr == displays.end()) { 69 if (itr == displays.end()) {
66 return {}; 70 return {};
67 } 71 }
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index 6081f41e1..c75b4ee34 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -12,10 +12,10 @@ namespace Service::PCTL {
12class IParentalControlService final : public ServiceFramework<IParentalControlService> { 12class IParentalControlService final : public ServiceFramework<IParentalControlService> {
13public: 13public:
14 IParentalControlService() : ServiceFramework("IParentalControlService") { 14 IParentalControlService() : ServiceFramework("IParentalControlService") {
15 // clang-format off
15 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
16 {1, &IParentalControlService::Initialize, "Initialize"}, 17 {1, &IParentalControlService::Initialize, "Initialize"},
17 {1001, &IParentalControlService::CheckFreeCommunicationPermission, 18 {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"},
18 "CheckFreeCommunicationPermission"},
19 {1002, nullptr, "ConfirmLaunchApplicationPermission"}, 19 {1002, nullptr, "ConfirmLaunchApplicationPermission"},
20 {1003, nullptr, "ConfirmResumeApplicationPermission"}, 20 {1003, nullptr, "ConfirmResumeApplicationPermission"},
21 {1004, nullptr, "ConfirmSnsPostPermission"}, 21 {1004, nullptr, "ConfirmSnsPostPermission"},
@@ -30,6 +30,7 @@ public:
30 {1013, nullptr, "ConfirmStereoVisionPermission"}, 30 {1013, nullptr, "ConfirmStereoVisionPermission"},
31 {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, 31 {1014, nullptr, "ConfirmPlayableApplicationVideoOld"},
32 {1015, nullptr, "ConfirmPlayableApplicationVideo"}, 32 {1015, nullptr, "ConfirmPlayableApplicationVideo"},
33 {1016, nullptr, "ConfirmShowNewsPermission"},
33 {1031, nullptr, "IsRestrictionEnabled"}, 34 {1031, nullptr, "IsRestrictionEnabled"},
34 {1032, nullptr, "GetSafetyLevel"}, 35 {1032, nullptr, "GetSafetyLevel"},
35 {1033, nullptr, "SetSafetyLevel"}, 36 {1033, nullptr, "SetSafetyLevel"},
@@ -45,6 +46,7 @@ public:
45 {1045, nullptr, "UpdateFreeCommunicationApplicationList"}, 46 {1045, nullptr, "UpdateFreeCommunicationApplicationList"},
46 {1046, nullptr, "DisableFeaturesForReset"}, 47 {1046, nullptr, "DisableFeaturesForReset"},
47 {1047, nullptr, "NotifyApplicationDownloadStarted"}, 48 {1047, nullptr, "NotifyApplicationDownloadStarted"},
49 {1048, nullptr, "NotifyNetworkProfileCreated"},
48 {1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"}, 50 {1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"},
49 {1062, nullptr, "GetStereoVisionRestriction"}, 51 {1062, nullptr, "GetStereoVisionRestriction"},
50 {1063, nullptr, "SetStereoVisionRestriction"}, 52 {1063, nullptr, "SetStereoVisionRestriction"},
@@ -63,6 +65,7 @@ public:
63 {1411, nullptr, "GetPairingAccountInfo"}, 65 {1411, nullptr, "GetPairingAccountInfo"},
64 {1421, nullptr, "GetAccountNickname"}, 66 {1421, nullptr, "GetAccountNickname"},
65 {1424, nullptr, "GetAccountState"}, 67 {1424, nullptr, "GetAccountState"},
68 {1425, nullptr, "RequestPostEvents"},
66 {1432, nullptr, "GetSynchronizationEvent"}, 69 {1432, nullptr, "GetSynchronizationEvent"},
67 {1451, nullptr, "StartPlayTimer"}, 70 {1451, nullptr, "StartPlayTimer"},
68 {1452, nullptr, "StopPlayTimer"}, 71 {1452, nullptr, "StopPlayTimer"},
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 6b27dc4a3..ebcc41a43 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -42,15 +42,18 @@ private:
42class DebugMonitor final : public ServiceFramework<DebugMonitor> { 42class DebugMonitor final : public ServiceFramework<DebugMonitor> {
43public: 43public:
44 explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} { 44 explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} {
45 // clang-format off
45 static const FunctionInfo functions[] = { 46 static const FunctionInfo functions[] = {
46 {0, nullptr, "IsDebugMode"}, 47 {0, nullptr, "GetDebugProcesses"},
47 {1, nullptr, "GetDebugProcesses"}, 48 {1, nullptr, "StartDebugProcess"},
48 {2, nullptr, "StartDebugProcess"}, 49 {2, nullptr, "GetTitlePid"},
49 {3, nullptr, "GetTitlePid"}, 50 {3, nullptr, "EnableDebugForTitleId"},
50 {4, nullptr, "EnableDebugForTitleId"}, 51 {4, nullptr, "GetApplicationPid"},
51 {5, nullptr, "GetApplicationPid"}, 52 {5, nullptr, "EnableDebugForApplication"},
52 {6, nullptr, "EnableDebugForApplication"}, 53 {6, nullptr, "DisableDebug"},
53 }; 54 };
55 // clang-format on
56
54 RegisterHandlers(functions); 57 RegisterHandlers(functions);
55 } 58 }
56}; 59};
@@ -68,6 +71,7 @@ public:
68class Shell final : public ServiceFramework<Shell> { 71class Shell final : public ServiceFramework<Shell> {
69public: 72public:
70 explicit Shell() : ServiceFramework{"pm:shell"} { 73 explicit Shell() : ServiceFramework{"pm:shell"} {
74 // clang-format off
71 static const FunctionInfo functions[] = { 75 static const FunctionInfo functions[] = {
72 {0, nullptr, "LaunchProcess"}, 76 {0, nullptr, "LaunchProcess"},
73 {1, nullptr, "TerminateProcessByPid"}, 77 {1, nullptr, "TerminateProcessByPid"},
@@ -77,7 +81,10 @@ public:
77 {5, nullptr, "NotifyBootFinished"}, 81 {5, nullptr, "NotifyBootFinished"},
78 {6, nullptr, "GetApplicationPid"}, 82 {6, nullptr, "GetApplicationPid"},
79 {7, nullptr, "BoostSystemMemoryResourceLimit"}, 83 {7, nullptr, "BoostSystemMemoryResourceLimit"},
84 {8, nullptr, "EnableAdditionalSystemThreads"},
80 }; 85 };
86 // clang-format on
87
81 RegisterHandlers(functions); 88 RegisterHandlers(functions);
82 } 89 }
83}; 90};
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 830790269..abbfe5524 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -90,7 +90,7 @@ private:
90 Kernel::HLERequestContext& ctx); 90 Kernel::HLERequestContext& ctx);
91 91
92 ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); 92 ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker);
93 ~ServiceFrameworkBase(); 93 ~ServiceFrameworkBase() override;
94 94
95 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); 95 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
96 void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); 96 void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info);
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 1afc43f75..4ecb6bcef 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -116,6 +116,7 @@ void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
116} 116}
117 117
118SET::SET() : ServiceFramework("set") { 118SET::SET() : ServiceFramework("set") {
119 // clang-format off
119 static const FunctionInfo functions[] = { 120 static const FunctionInfo functions[] = {
120 {0, &SET::GetLanguageCode, "GetLanguageCode"}, 121 {0, &SET::GetLanguageCode, "GetLanguageCode"},
121 {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, 122 {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
@@ -126,7 +127,10 @@ SET::SET() : ServiceFramework("set") {
126 {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, 127 {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
127 {7, nullptr, "GetKeyCodeMap"}, 128 {7, nullptr, "GetKeyCodeMap"},
128 {8, nullptr, "GetQuestFlag"}, 129 {8, nullptr, "GetQuestFlag"},
130 {9, nullptr, "GetKeyCodeMap2"},
129 }; 131 };
132 // clang-format on
133
130 RegisterHandlers(functions); 134 RegisterHandlers(functions);
131} 135}
132 136
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index 34654bb07..5981c575c 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -40,7 +40,7 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
40 {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, 40 {30, nullptr, "GetAmiiboEcqvBlsCertificate"},
41 {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, 41 {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"},
42 {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"}, 42 {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"},
43 {33, nullptr, "GetBatteryVersion"}, 43 {41, nullptr, "GetBatteryVersion"},
44 }; 44 };
45 RegisterHandlers(functions); 45 RegisterHandlers(functions);
46} 46}
diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h
index 583036eac..a0677e815 100644
--- a/src/core/hle/service/set/set_cal.h
+++ b/src/core/hle/service/set/set_cal.h
@@ -11,7 +11,7 @@ namespace Service::Set {
11class SET_CAL final : public ServiceFramework<SET_CAL> { 11class SET_CAL final : public ServiceFramework<SET_CAL> {
12public: 12public:
13 explicit SET_CAL(); 13 explicit SET_CAL();
14 ~SET_CAL(); 14 ~SET_CAL() override;
15}; 15};
16 16
17} // namespace Service::Set 17} // namespace Service::Set
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index ecee554bf..98d0cfdfd 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -104,6 +104,7 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) {
104} 104}
105 105
106SET_SYS::SET_SYS() : ServiceFramework("set:sys") { 106SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
107 // clang-format off
107 static const FunctionInfo functions[] = { 108 static const FunctionInfo functions[] = {
108 {0, nullptr, "SetLanguageCode"}, 109 {0, nullptr, "SetLanguageCode"},
109 {1, nullptr, "SetNetworkSettings"}, 110 {1, nullptr, "SetNetworkSettings"},
@@ -252,7 +253,33 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
252 {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, 253 {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"},
253 {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, 254 {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"},
254 {149, nullptr, "GetRebootlessSystemUpdateVersion"}, 255 {149, nullptr, "GetRebootlessSystemUpdateVersion"},
256 {150, nullptr, "GetDeviceTimeZoneLocationUpdatedTime"},
257 {151, nullptr, "SetDeviceTimeZoneLocationUpdatedTime"},
258 {152, nullptr, "GetUserSystemClockAutomaticCorrectionUpdatedTime"},
259 {153, nullptr, "SetUserSystemClockAutomaticCorrectionUpdatedTime"},
260 {154, nullptr, "GetAccountOnlineStorageSettings"},
261 {155, nullptr, "SetAccountOnlineStorageSettings"},
262 {156, nullptr, "GetPctlReadyFlag"},
263 {157, nullptr, "SetPctlReadyFlag"},
264 {162, nullptr, "GetPtmBatteryVersion"},
265 {163, nullptr, "SetPtmBatteryVersion"},
266 {164, nullptr, "GetUsb30HostEnableFlag"},
267 {165, nullptr, "SetUsb30HostEnableFlag"},
268 {166, nullptr, "GetUsb30DeviceEnableFlag"},
269 {167, nullptr, "SetUsb30DeviceEnableFlag"},
270 {168, nullptr, "GetThemeId"},
271 {169, nullptr, "SetThemeId"},
272 {170, nullptr, "GetChineseTraditionalInputMethod"},
273 {171, nullptr, "SetChineseTraditionalInputMethod"},
274 {172, nullptr, "GetPtmCycleCountReliability"},
275 {173, nullptr, "SetPtmCycleCountReliability"},
276 {175, nullptr, "GetThemeSettings"},
277 {176, nullptr, "SetThemeSettings"},
278 {177, nullptr, "GetThemeKey"},
279 {178, nullptr, "SetThemeKey"},
255 }; 280 };
281 // clang-format on
282
256 RegisterHandlers(functions); 283 RegisterHandlers(functions);
257} 284}
258 285
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 4342f3b2d..884ad173b 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -73,6 +73,7 @@ void BSD::Close(Kernel::HLERequestContext& ctx) {
73} 73}
74 74
75BSD::BSD(const char* name) : ServiceFramework(name) { 75BSD::BSD(const char* name) : ServiceFramework(name) {
76 // clang-format off
76 static const FunctionInfo functions[] = { 77 static const FunctionInfo functions[] = {
77 {0, &BSD::RegisterClient, "RegisterClient"}, 78 {0, &BSD::RegisterClient, "RegisterClient"},
78 {1, &BSD::StartMonitoring, "StartMonitoring"}, 79 {1, &BSD::StartMonitoring, "StartMonitoring"},
@@ -105,7 +106,11 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
105 {28, nullptr, "GetResourceStatistics"}, 106 {28, nullptr, "GetResourceStatistics"},
106 {29, nullptr, "RecvMMsg"}, 107 {29, nullptr, "RecvMMsg"},
107 {30, nullptr, "SendMMsg"}, 108 {30, nullptr, "SendMMsg"},
109 {31, nullptr, "EventFd"},
110 {32, nullptr, "RegisterResourceStatisticsName"},
108 }; 111 };
112 // clang-format on
113
109 RegisterHandlers(functions); 114 RegisterHandlers(functions);
110} 115}
111 116
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index 13ab1d31e..852e71e4b 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -8,12 +8,20 @@
8namespace Service::Sockets { 8namespace Service::Sockets {
9 9
10void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) { 10void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
11 struct Parameters {
12 u8 use_nsd_resolve;
13 u32 unknown;
14 u64 process_id;
15 };
16
11 IPC::RequestParser rp{ctx}; 17 IPC::RequestParser rp{ctx};
18 const auto parameters = rp.PopRaw<Parameters>();
12 19
13 LOG_WARNING(Service, "(STUBBED) called"); 20 LOG_WARNING(Service,
21 "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}",
22 parameters.use_nsd_resolve, parameters.unknown, parameters.process_id);
14 23
15 IPC::ResponseBuilder rb{ctx, 2}; 24 IPC::ResponseBuilder rb{ctx, 2};
16
17 rb.Push(RESULT_SUCCESS); 25 rb.Push(RESULT_SUCCESS);
18} 26}
19 27
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index 8db0c2f13..e724d4ab8 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -26,9 +26,7 @@ Module::Interface::~Interface() = default;
26void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { 26void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
27 LOG_DEBUG(Service_SPL, "called"); 27 LOG_DEBUG(Service_SPL, "called");
28 28
29 IPC::RequestParser rp{ctx}; 29 const std::size_t size = ctx.GetWriteBufferSize();
30
31 std::size_t size = ctx.GetWriteBufferSize();
32 30
33 std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max()); 31 std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
34 std::vector<u8> data(size); 32 std::vector<u8> data(size);
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index af40a1815..65040c077 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -64,13 +64,19 @@ public:
64 }; 64 };
65 RegisterHandlers(functions); 65 RegisterHandlers(functions);
66 } 66 }
67 ~ISslContext() = default;
68 67
69private: 68private:
70 void SetOption(Kernel::HLERequestContext& ctx) { 69 void SetOption(Kernel::HLERequestContext& ctx) {
71 LOG_WARNING(Service_SSL, "(STUBBED) called"); 70 struct Parameters {
71 u8 enable;
72 u32 option;
73 };
72 74
73 IPC::RequestParser rp{ctx}; 75 IPC::RequestParser rp{ctx};
76 const auto parameters = rp.PopRaw<Parameters>();
77
78 LOG_WARNING(Service_SSL, "(STUBBED) called. enable={}, option={}", parameters.enable,
79 parameters.option);
74 80
75 IPC::ResponseBuilder rb{ctx, 2}; 81 IPC::ResponseBuilder rb{ctx, 2};
76 rb.Push(RESULT_SUCCESS); 82 rb.Push(RESULT_SUCCESS);
@@ -97,6 +103,8 @@ public:
97 {4, nullptr, "DebugIoctl"}, 103 {4, nullptr, "DebugIoctl"},
98 {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"}, 104 {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"},
99 {6, nullptr, "FlushSessionCache"}, 105 {6, nullptr, "FlushSessionCache"},
106 {7, nullptr, "SetDebugOption"},
107 {8, nullptr, "GetDebugOption"},
100 }; 108 };
101 // clang-format on 109 // clang-format on
102 110
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index b3a196f65..8d122ae33 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -8,6 +8,7 @@ namespace Service::Time {
8 8
9Time::Time(std::shared_ptr<Module> time, const char* name) 9Time::Time(std::shared_ptr<Module> time, const char* name)
10 : Module::Interface(std::move(time), name) { 10 : Module::Interface(std::move(time), name) {
11 // clang-format off
11 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
12 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, 13 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
13 {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, 14 {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
@@ -15,18 +16,23 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
15 {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, 16 {3, &Time::GetTimeZoneService, "GetTimeZoneService"},
16 {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, 17 {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
17 {5, nullptr, "GetEphemeralNetworkSystemClock"}, 18 {5, nullptr, "GetEphemeralNetworkSystemClock"},
19 {20, nullptr, "GetSharedMemoryNativeHandle"},
20 {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"},
21 {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
18 {50, nullptr, "SetStandardSteadyClockInternalOffset"}, 22 {50, nullptr, "SetStandardSteadyClockInternalOffset"},
19 {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, 23 {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
20 {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, 24 {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
21 {102, nullptr, "GetStandardUserSystemClockInitialYear"}, 25 {102, nullptr, "GetStandardUserSystemClockInitialYear"},
22 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, 26 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
27 {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, 28 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, 29 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, 30 {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
26 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, 31 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
27 "CalculateStandardUserSystemClockDifferenceByUser"},
28 {501, nullptr, "CalculateSpanBetween"}, 32 {501, nullptr, "CalculateSpanBetween"},
29 }; 33 };
34 // clang-format on
35
30 RegisterHandlers(functions); 36 RegisterHandlers(functions);
31} 37}
32 38
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 566cd6006..4e17249a9 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -498,7 +498,6 @@ public:
498 }; 498 };
499 RegisterHandlers(functions); 499 RegisterHandlers(functions);
500 } 500 }
501 ~IHOSBinderDriver() = default;
502 501
503private: 502private:
504 enum class TransactionId { 503 enum class TransactionId {
@@ -692,7 +691,6 @@ public:
692 }; 691 };
693 RegisterHandlers(functions); 692 RegisterHandlers(functions);
694 } 693 }
695 ~ISystemDisplayService() = default;
696 694
697private: 695private:
698 void SetLayerZ(Kernel::HLERequestContext& ctx) { 696 void SetLayerZ(Kernel::HLERequestContext& ctx) {
@@ -818,7 +816,6 @@ public:
818 }; 816 };
819 RegisterHandlers(functions); 817 RegisterHandlers(functions);
820 } 818 }
821 ~IManagerDisplayService() = default;
822 819
823private: 820private:
824 void CloseDisplay(Kernel::HLERequestContext& ctx) { 821 void CloseDisplay(Kernel::HLERequestContext& ctx) {
@@ -884,7 +881,6 @@ private:
884class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 881class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
885public: 882public:
886 explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 883 explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
887 ~IApplicationDisplayService() = default;
888 884
889private: 885private:
890 enum class ConvertedScaleMode : u64 { 886 enum class ConvertedScaleMode : u64 {
@@ -1037,7 +1033,6 @@ private:
1037 void ListDisplays(Kernel::HLERequestContext& ctx) { 1033 void ListDisplays(Kernel::HLERequestContext& ctx) {
1038 LOG_WARNING(Service_VI, "(STUBBED) called"); 1034 LOG_WARNING(Service_VI, "(STUBBED) called");
1039 1035
1040 IPC::RequestParser rp{ctx};
1041 DisplayInfo display_info; 1036 DisplayInfo display_info;
1042 display_info.width *= static_cast<u64>(Settings::values.resolution_factor); 1037 display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
1043 display_info.height *= static_cast<u64>(Settings::values.resolution_factor); 1038 display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index babc7e646..d7c47c197 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -4,11 +4,12 @@
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include <vector> 6#include <vector>
7#include <lz4.h> 7
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "common/hex_util.h" 10#include "common/hex_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/lz4_compression.h"
12#include "common/swap.h" 13#include "common/swap.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/file_sys/patch_manager.h" 15#include "core/file_sys/patch_manager.h"
@@ -20,6 +21,8 @@
20#include "core/memory.h" 21#include "core/memory.h"
21#include "core/settings.h" 22#include "core/settings.h"
22 23
24#pragma optimize("", off)
25
23namespace Loader { 26namespace Loader {
24namespace { 27namespace {
25struct MODHeader { 28struct MODHeader {
@@ -35,15 +38,11 @@ static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
35 38
36std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, 39std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
37 const NSOSegmentHeader& header) { 40 const NSOSegmentHeader& header) {
38 std::vector<u8> uncompressed_data(header.size); 41 const std::vector<u8> uncompressed_data =
39 const int bytes_uncompressed = 42 Common::Compression::DecompressDataLZ4(compressed_data, header.size);
40 LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
41 reinterpret_cast<char*>(uncompressed_data.data()),
42 static_cast<int>(compressed_data.size()), header.size);
43 43
44 ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) && 44 ASSERT_MSG(uncompressed_data.size() == static_cast<int>(header.size), "{} != {}", header.size,
45 bytes_uncompressed == static_cast<int>(uncompressed_data.size()), 45 uncompressed_data.size());
46 "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
47 46
48 return uncompressed_data; 47 return uncompressed_data;
49} 48}
@@ -139,13 +138,13 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
139 138
140 // Apply patches if necessary 139 // Apply patches if necessary
141 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { 140 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
142 std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size()); 141 std::vector<u8> pi_header;
143 pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header), 142 pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
144 reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader)); 143 reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
145 pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(), 144 pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
146 program_image.end()); 145 program_image.end());
147 146
148 pi_header = pm->PatchNSO(pi_header); 147 pi_header = pm->PatchNSO(pi_header, file.GetName());
149 148
150 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin()); 149 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
151 } 150 }
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index d6995b61e..436f7387c 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -22,7 +22,7 @@ class AppLoader_NCA;
22class AppLoader_XCI final : public AppLoader { 22class AppLoader_XCI final : public AppLoader {
23public: 23public:
24 explicit AppLoader_XCI(FileSys::VirtualFile file); 24 explicit AppLoader_XCI(FileSys::VirtualFile file);
25 ~AppLoader_XCI(); 25 ~AppLoader_XCI() override;
26 26
27 /** 27 /**
28 * Returns the type of the file 28 * Returns the type of the file
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 332c1037c..4e0538bc2 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -38,10 +38,6 @@ void SetCurrentPageTable(Common::PageTable* page_table) {
38 } 38 }
39} 39}
40 40
41Common::PageTable* GetCurrentPageTable() {
42 return current_page_table;
43}
44
45static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory, 41static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
46 Common::PageType type) { 42 Common::PageType type) {
47 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, 43 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
diff --git a/src/core/memory.h b/src/core/memory.h
index 1d38cdca8..6845f5fe1 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -28,16 +28,6 @@ constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
28 28
29/// Virtual user-space memory regions 29/// Virtual user-space memory regions
30enum : VAddr { 30enum : VAddr {
31 /// Read-only page containing kernel and system configuration values.
32 CONFIG_MEMORY_VADDR = 0x1FF80000,
33 CONFIG_MEMORY_SIZE = 0x00001000,
34 CONFIG_MEMORY_VADDR_END = CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE,
35
36 /// Usually read-only page containing mostly values read from hardware.
37 SHARED_PAGE_VADDR = 0x1FF81000,
38 SHARED_PAGE_SIZE = 0x00001000,
39 SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE,
40
41 /// TLS (Thread-Local Storage) related. 31 /// TLS (Thread-Local Storage) related.
42 TLS_ENTRY_SIZE = 0x200, 32 TLS_ENTRY_SIZE = 0x200,
43 33
@@ -50,9 +40,8 @@ enum : VAddr {
50 KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, 40 KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
51}; 41};
52 42
53/// Currently active page table 43/// Changes the currently active page table.
54void SetCurrentPageTable(Common::PageTable* page_table); 44void SetCurrentPageTable(Common::PageTable* page_table);
55Common::PageTable* GetCurrentPageTable();
56 45
57/// Determines if the given VAddr is valid for the specified process. 46/// Determines if the given VAddr is valid for the specified process.
58bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); 47bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
diff --git a/src/core/settings.h b/src/core/settings.h
index d543eb32f..b84390745 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -393,6 +393,7 @@ struct Values {
393 bool use_disk_shader_cache; 393 bool use_disk_shader_cache;
394 bool use_accurate_gpu_emulation; 394 bool use_accurate_gpu_emulation;
395 bool use_asynchronous_gpu_emulation; 395 bool use_asynchronous_gpu_emulation;
396 bool force_30fps_mode;
396 397
397 float bg_red; 398 float bg_red;
398 float bg_green; 399 float bg_green;
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 3e1a735c3..58af41f6e 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -17,7 +17,6 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
17 : mutable_memory(mutable_memory_), 17 : mutable_memory(mutable_memory_),
18 test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} { 18 test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} {
19 auto process = Kernel::Process::Create(Core::System::GetInstance(), ""); 19 auto process = Kernel::Process::Create(Core::System::GetInstance(), "");
20 kernel.MakeCurrentProcess(process.get());
21 page_table = &process->VMManager().page_table; 20 page_table = &process->VMManager().page_table;
22 21
23 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); 22 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
@@ -28,7 +27,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
28 Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); 27 Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
29 Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); 28 Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
30 29
31 Memory::SetCurrentPageTable(page_table); 30 kernel.MakeCurrentProcess(process.get());
32} 31}
33 32
34TestEnvironment::~TestEnvironment() { 33TestEnvironment::~TestEnvironment() {
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 44c761d3e..114bed20d 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -106,6 +106,8 @@ add_library(video_core STATIC
106 textures/decoders.cpp 106 textures/decoders.cpp
107 textures/decoders.h 107 textures/decoders.h
108 textures/texture.h 108 textures/texture.h
109 texture_cache.cpp
110 texture_cache.h
109 video_core.cpp 111 video_core.cpp
110 video_core.h 112 video_core.h
111) 113)
@@ -127,16 +129,21 @@ if (ENABLE_VULKAN)
127 renderer_vulkan/vk_sampler_cache.h 129 renderer_vulkan/vk_sampler_cache.h
128 renderer_vulkan/vk_scheduler.cpp 130 renderer_vulkan/vk_scheduler.cpp
129 renderer_vulkan/vk_scheduler.h 131 renderer_vulkan/vk_scheduler.h
132 renderer_vulkan/vk_shader_decompiler.cpp
133 renderer_vulkan/vk_shader_decompiler.h
130 renderer_vulkan/vk_stream_buffer.cpp 134 renderer_vulkan/vk_stream_buffer.cpp
131 renderer_vulkan/vk_stream_buffer.h 135 renderer_vulkan/vk_stream_buffer.h
132 renderer_vulkan/vk_swapchain.cpp 136 renderer_vulkan/vk_swapchain.cpp
133 renderer_vulkan/vk_swapchain.h) 137 renderer_vulkan/vk_swapchain.h)
134 138
135 target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) 139 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
136 target_compile_definitions(video_core PRIVATE HAS_VULKAN) 140 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
137endif() 141endif()
138 142
139create_target_directory_groups(video_core) 143create_target_directory_groups(video_core)
140 144
141target_link_libraries(video_core PUBLIC common core) 145target_link_libraries(video_core PUBLIC common core)
142target_link_libraries(video_core PRIVATE glad lz4_static) 146target_link_libraries(video_core PRIVATE glad)
147if (ENABLE_VULKAN)
148 target_link_libraries(video_core PRIVATE sirit)
149endif()
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 8b1bea1ae..046d047cb 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -8,6 +8,7 @@
8#include "video_core/dma_pusher.h" 8#include "video_core/dma_pusher.h"
9#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/gpu.h" 10#include "video_core/gpu.h"
11#include "video_core/memory_manager.h"
11 12
12namespace Tegra { 13namespace Tegra {
13 14
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 03b7ee5d8..55966eef1 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -6,12 +6,13 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/math_util.h" 7#include "common/math_util.h"
8#include "video_core/engines/fermi_2d.h" 8#include "video_core/engines/fermi_2d.h"
9#include "video_core/memory_manager.h"
9#include "video_core/rasterizer_interface.h" 10#include "video_core/rasterizer_interface.h"
10 11
11namespace Tegra::Engines { 12namespace Tegra::Engines {
12 13
13Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) 14Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
14 : memory_manager(memory_manager), rasterizer{rasterizer} {} 15 : rasterizer{rasterizer}, memory_manager{memory_manager} {}
15 16
16void Fermi2D::CallMethod(const GPU::MethodCall& method_call) { 17void Fermi2D::CallMethod(const GPU::MethodCall& method_call) {
17 ASSERT_MSG(method_call.method < Regs::NUM_REGS, 18 ASSERT_MSG(method_call.method < Regs::NUM_REGS,
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 80523e320..2e51b7f13 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -10,7 +10,10 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/gpu.h" 12#include "video_core/gpu.h"
13#include "video_core/memory_manager.h" 13
14namespace Tegra {
15class MemoryManager;
16}
14 17
15namespace VideoCore { 18namespace VideoCore {
16class RasterizerInterface; 19class RasterizerInterface;
@@ -115,10 +118,9 @@ public:
115 }; 118 };
116 } regs{}; 119 } regs{};
117 120
118 MemoryManager& memory_manager;
119
120private: 121private:
121 VideoCore::RasterizerInterface& rasterizer; 122 VideoCore::RasterizerInterface& rasterizer;
123 MemoryManager& memory_manager;
122 124
123 /// Performs the copy from the source surface to the destination surface as configured in the 125 /// Performs the copy from the source surface to the destination surface as configured in the
124 /// registers. 126 /// registers.
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index 6575afd0f..fb6cdf432 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -9,7 +9,10 @@
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/gpu.h" 11#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 12
13namespace Tegra {
14class MemoryManager;
15}
13 16
14namespace Tegra::Engines { 17namespace Tegra::Engines {
15 18
@@ -40,10 +43,11 @@ public:
40 static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), 43 static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32),
41 "KeplerCompute Regs has wrong size"); 44 "KeplerCompute Regs has wrong size");
42 45
43 MemoryManager& memory_manager;
44
45 /// Write the value to the register identified by method. 46 /// Write the value to the register identified by method.
46 void CallMethod(const GPU::MethodCall& method_call); 47 void CallMethod(const GPU::MethodCall& method_call);
48
49private:
50 MemoryManager& memory_manager;
47}; 51};
48 52
49#define ASSERT_REG_POSITION(field_name, position) \ 53#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index e259bf46b..cd51a31d7 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -5,9 +5,9 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/memory.h"
9#include "video_core/engines/kepler_memory.h" 8#include "video_core/engines/kepler_memory.h"
10#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/memory_manager.h"
11#include "video_core/rasterizer_interface.h" 11#include "video_core/rasterizer_interface.h"
12#include "video_core/renderer_base.h" 12#include "video_core/renderer_base.h"
13 13
@@ -15,7 +15,7 @@ namespace Tegra::Engines {
15 15
16KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 16KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
17 MemoryManager& memory_manager) 17 MemoryManager& memory_manager)
18 : system{system}, memory_manager(memory_manager), rasterizer{rasterizer} {} 18 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {}
19 19
20KeplerMemory::~KeplerMemory() = default; 20KeplerMemory::~KeplerMemory() = default;
21 21
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index 9181e9d80..78b6c3e45 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -10,12 +10,15 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/gpu.h" 12#include "video_core/gpu.h"
13#include "video_core/memory_manager.h"
14 13
15namespace Core { 14namespace Core {
16class System; 15class System;
17} 16}
18 17
18namespace Tegra {
19class MemoryManager;
20}
21
19namespace VideoCore { 22namespace VideoCore {
20class RasterizerInterface; 23class RasterizerInterface;
21} 24}
@@ -82,8 +85,8 @@ public:
82 85
83private: 86private:
84 Core::System& system; 87 Core::System& system;
85 MemoryManager& memory_manager;
86 VideoCore::RasterizerInterface& rasterizer; 88 VideoCore::RasterizerInterface& rasterizer;
89 MemoryManager& memory_manager;
87 90
88 void ProcessData(u32 data); 91 void ProcessData(u32 data);
89}; 92};
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index defcfbd3f..74403eed4 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -7,11 +7,10 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/core_timing.h" 9#include "core/core_timing.h"
10#include "core/memory.h"
11#include "video_core/debug_utils/debug_utils.h" 10#include "video_core/debug_utils/debug_utils.h"
12#include "video_core/engines/maxwell_3d.h" 11#include "video_core/engines/maxwell_3d.h"
12#include "video_core/memory_manager.h"
13#include "video_core/rasterizer_interface.h" 13#include "video_core/rasterizer_interface.h"
14#include "video_core/renderer_base.h"
15#include "video_core/textures/texture.h" 14#include "video_core/textures/texture.h"
16 15
17namespace Tegra::Engines { 16namespace Tegra::Engines {
@@ -21,8 +20,8 @@ constexpr u32 MacroRegistersStart = 0xE00;
21 20
22Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 21Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
23 MemoryManager& memory_manager) 22 MemoryManager& memory_manager)
24 : memory_manager(memory_manager), system{system}, rasterizer{rasterizer}, 23 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, macro_interpreter{
25 macro_interpreter(*this) { 24 *this} {
26 InitializeRegisterDefaults(); 25 InitializeRegisterDefaults();
27} 26}
28 27
@@ -250,6 +249,10 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
250 ProcessQueryGet(); 249 ProcessQueryGet();
251 break; 250 break;
252 } 251 }
252 case MAXWELL3D_REG_INDEX(sync_info): {
253 ProcessSyncPoint();
254 break;
255 }
253 default: 256 default:
254 break; 257 break;
255 } 258 }
@@ -327,6 +330,14 @@ void Maxwell3D::ProcessQueryGet() {
327 } 330 }
328} 331}
329 332
333void Maxwell3D::ProcessSyncPoint() {
334 const u32 sync_point = regs.sync_info.sync_point.Value();
335 const u32 increment = regs.sync_info.increment.Value();
336 const u32 cache_flush = regs.sync_info.unknown.Value();
337 LOG_DEBUG(HW_GPU, "Syncpoint set {}, increment: {}, unk: {}", sync_point, increment,
338 cache_flush);
339}
340
330void Maxwell3D::DrawArrays() { 341void Maxwell3D::DrawArrays() {
331 LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()), 342 LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
332 regs.vertex_buffer.count); 343 regs.vertex_buffer.count);
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 7fbf1026e..321af3297 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -16,13 +16,16 @@
16#include "common/math_util.h" 16#include "common/math_util.h"
17#include "video_core/gpu.h" 17#include "video_core/gpu.h"
18#include "video_core/macro_interpreter.h" 18#include "video_core/macro_interpreter.h"
19#include "video_core/memory_manager.h"
20#include "video_core/textures/texture.h" 19#include "video_core/textures/texture.h"
21 20
22namespace Core { 21namespace Core {
23class System; 22class System;
24} 23}
25 24
25namespace Tegra {
26class MemoryManager;
27}
28
26namespace VideoCore { 29namespace VideoCore {
27class RasterizerInterface; 30class RasterizerInterface;
28} 31}
@@ -576,7 +579,17 @@ public:
576 u32 bind; 579 u32 bind;
577 } macros; 580 } macros;
578 581
579 INSERT_PADDING_WORDS(0x188); 582 INSERT_PADDING_WORDS(0x69);
583
584 struct {
585 union {
586 BitField<0, 16, u32> sync_point;
587 BitField<16, 1, u32> unknown;
588 BitField<20, 1, u32> increment;
589 };
590 } sync_info;
591
592 INSERT_PADDING_WORDS(0x11E);
580 593
581 u32 tfb_enabled; 594 u32 tfb_enabled;
582 595
@@ -1093,7 +1106,6 @@ public:
1093 }; 1106 };
1094 1107
1095 State state{}; 1108 State state{};
1096 MemoryManager& memory_manager;
1097 1109
1098 struct DirtyFlags { 1110 struct DirtyFlags {
1099 std::bitset<8> color_buffer{0xFF}; 1111 std::bitset<8> color_buffer{0xFF};
@@ -1141,6 +1153,8 @@ private:
1141 1153
1142 VideoCore::RasterizerInterface& rasterizer; 1154 VideoCore::RasterizerInterface& rasterizer;
1143 1155
1156 MemoryManager& memory_manager;
1157
1144 /// Start offsets of each macro in macro_memory 1158 /// Start offsets of each macro in macro_memory
1145 std::unordered_map<u32, u32> macro_offsets; 1159 std::unordered_map<u32, u32> macro_offsets;
1146 1160
@@ -1180,6 +1194,9 @@ private:
1180 /// Handles a write to the QUERY_GET register. 1194 /// Handles a write to the QUERY_GET register.
1181 void ProcessQueryGet(); 1195 void ProcessQueryGet();
1182 1196
1197 /// Handles writes to syncing register.
1198 void ProcessSyncPoint();
1199
1183 /// Handles a write to the CB_DATA[i] register. 1200 /// Handles a write to the CB_DATA[i] register.
1184 void ProcessCBData(u32 value); 1201 void ProcessCBData(u32 value);
1185 1202
@@ -1195,6 +1212,7 @@ private:
1195 "Field " #field_name " has invalid position") 1212 "Field " #field_name " has invalid position")
1196 1213
1197ASSERT_REG_POSITION(macros, 0x45); 1214ASSERT_REG_POSITION(macros, 0x45);
1215ASSERT_REG_POSITION(sync_info, 0xB2);
1198ASSERT_REG_POSITION(tfb_enabled, 0x1D1); 1216ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
1199ASSERT_REG_POSITION(rt, 0x200); 1217ASSERT_REG_POSITION(rt, 0x200);
1200ASSERT_REG_POSITION(viewport_transform, 0x280); 1218ASSERT_REG_POSITION(viewport_transform, 0x280);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 5cca5c29a..2426d0067 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -5,9 +5,9 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/memory.h"
9#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
10#include "video_core/engines/maxwell_dma.h" 9#include "video_core/engines/maxwell_dma.h"
10#include "video_core/memory_manager.h"
11#include "video_core/rasterizer_interface.h" 11#include "video_core/rasterizer_interface.h"
12#include "video_core/renderer_base.h" 12#include "video_core/renderer_base.h"
13#include "video_core/textures/decoders.h" 13#include "video_core/textures/decoders.h"
@@ -16,7 +16,7 @@ namespace Tegra::Engines {
16 16
17MaxwellDMA::MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 17MaxwellDMA::MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
18 MemoryManager& memory_manager) 18 MemoryManager& memory_manager)
19 : memory_manager(memory_manager), system{system}, rasterizer{rasterizer} {} 19 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {}
20 20
21void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) { 21void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) {
22 ASSERT_MSG(method_call.method < Regs::NUM_REGS, 22 ASSERT_MSG(method_call.method < Regs::NUM_REGS,
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 34c369320..c6b649842 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -10,12 +10,15 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/gpu.h" 12#include "video_core/gpu.h"
13#include "video_core/memory_manager.h"
14 13
15namespace Core { 14namespace Core {
16class System; 15class System;
17} 16}
18 17
18namespace Tegra {
19class MemoryManager;
20}
21
19namespace VideoCore { 22namespace VideoCore {
20class RasterizerInterface; 23class RasterizerInterface;
21} 24}
@@ -139,13 +142,13 @@ public:
139 }; 142 };
140 } regs{}; 143 } regs{};
141 144
142 MemoryManager& memory_manager;
143
144private: 145private:
145 Core::System& system; 146 Core::System& system;
146 147
147 VideoCore::RasterizerInterface& rasterizer; 148 VideoCore::RasterizerInterface& rasterizer;
148 149
150 MemoryManager& memory_manager;
151
149 /// Performs the copy from the source buffer to the destination buffer as configured in the 152 /// Performs the copy from the source buffer to the destination buffer as configured in the
150 /// registers. 153 /// registers.
151 void HandleCopy(); 154 void HandleCopy();
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index c41e3f4f0..38db4addd 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -806,6 +806,12 @@ union Instruction {
806 } ldg; 806 } ldg;
807 807
808 union { 808 union {
809 BitField<48, 3, UniformType> type;
810 BitField<46, 2, u64> cache_mode;
811 BitField<20, 24, s64> immediate_offset;
812 } stg;
813
814 union {
809 BitField<0, 3, u64> pred0; 815 BitField<0, 3, u64> pred0;
810 BitField<3, 3, u64> pred3; 816 BitField<3, 3, u64> pred3;
811 BitField<7, 1, u64> abs_a; 817 BitField<7, 1, u64> abs_a;
@@ -1252,13 +1258,16 @@ union Instruction {
1252 1258
1253 union { 1259 union {
1254 BitField<20, 16, u64> imm20_16; 1260 BitField<20, 16, u64> imm20_16;
1261 BitField<35, 1, u64> high_b_rr; // used on RR
1255 BitField<36, 1, u64> product_shift_left; 1262 BitField<36, 1, u64> product_shift_left;
1256 BitField<37, 1, u64> merge_37; 1263 BitField<37, 1, u64> merge_37;
1257 BitField<48, 1, u64> sign_a; 1264 BitField<48, 1, u64> sign_a;
1258 BitField<49, 1, u64> sign_b; 1265 BitField<49, 1, u64> sign_b;
1266 BitField<50, 2, XmadMode> mode_cbf; // used by CR, RC
1259 BitField<50, 3, XmadMode> mode; 1267 BitField<50, 3, XmadMode> mode;
1260 BitField<52, 1, u64> high_b; 1268 BitField<52, 1, u64> high_b;
1261 BitField<53, 1, u64> high_a; 1269 BitField<53, 1, u64> high_a;
1270 BitField<55, 1, u64> product_shift_left_second; // used on CR
1262 BitField<56, 1, u64> merge_56; 1271 BitField<56, 1, u64> merge_56;
1263 } xmad; 1272 } xmad;
1264 1273
@@ -1676,7 +1685,7 @@ private:
1676 INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"), 1685 INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"),
1677 INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), 1686 INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"),
1678 INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"), 1687 INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"),
1679 INST("0011100001000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"), 1688 INST("0011100-01000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"),
1680 INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "LOP32I"), 1689 INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "LOP32I"),
1681 INST("0000001---------", Id::LOP3_C, Type::ArithmeticInteger, "LOP3_C"), 1690 INST("0000001---------", Id::LOP3_C, Type::ArithmeticInteger, "LOP3_C"),
1682 INST("0101101111100---", Id::LOP3_R, Type::ArithmeticInteger, "LOP3_R"), 1691 INST("0101101111100---", Id::LOP3_R, Type::ArithmeticInteger, "LOP3_R"),
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 30b29e14d..4461083ff 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -31,7 +31,7 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
31 31
32GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} { 32GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} {
33 auto& rasterizer{renderer.Rasterizer()}; 33 auto& rasterizer{renderer.Rasterizer()};
34 memory_manager = std::make_unique<Tegra::MemoryManager>(); 34 memory_manager = std::make_unique<Tegra::MemoryManager>(rasterizer);
35 dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); 35 dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
36 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); 36 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
37 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); 37 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 8b355cf7b..db507cf04 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -9,7 +9,7 @@
9namespace VideoCommon { 9namespace VideoCommon {
10 10
11GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) 11GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
12 : Tegra::GPU(system, renderer), gpu_thread{renderer, *dma_pusher} {} 12 : Tegra::GPU(system, renderer), gpu_thread{system, renderer, *dma_pusher} {}
13 13
14GPUAsynch::~GPUAsynch() = default; 14GPUAsynch::~GPUAsynch() = default;
15 15
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index c5dc199c5..cc56cf467 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -4,6 +4,9 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "core/core.h"
8#include "core/core_timing.h"
9#include "core/core_timing_util.h"
7#include "core/frontend/scope_acquire_window_context.h" 10#include "core/frontend/scope_acquire_window_context.h"
8#include "video_core/dma_pusher.h" 11#include "video_core/dma_pusher.h"
9#include "video_core/gpu.h" 12#include "video_core/gpu.h"
@@ -36,7 +39,6 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
36 dma_pusher.Push(std::move(submit_list->entries)); 39 dma_pusher.Push(std::move(submit_list->entries));
37 dma_pusher.DispatchCalls(); 40 dma_pusher.DispatchCalls();
38 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) { 41 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
39 state.DecrementFramesCounter();
40 renderer.SwapBuffers(std::move(data->framebuffer)); 42 renderer.SwapBuffers(std::move(data->framebuffer));
41 } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) { 43 } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) {
42 renderer.Rasterizer().FlushRegion(data->addr, data->size); 44 renderer.Rasterizer().FlushRegion(data->addr, data->size);
@@ -47,13 +49,18 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
47 } else { 49 } else {
48 UNREACHABLE(); 50 UNREACHABLE();
49 } 51 }
52 state.signaled_fence = next.fence;
53 state.TrySynchronize();
50 } 54 }
51 } 55 }
52} 56}
53 57
54ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) 58ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
55 : renderer{renderer}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), 59 Tegra::DmaPusher& dma_pusher)
56 std::ref(state)} {} 60 : system{system}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)} {
61 synchronization_event = system.CoreTiming().RegisterEvent(
62 "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
63}
57 64
58ThreadManager::~ThreadManager() { 65ThreadManager::~ThreadManager() {
59 // Notify GPU thread that a shutdown is pending 66 // Notify GPU thread that a shutdown is pending
@@ -62,14 +69,14 @@ ThreadManager::~ThreadManager() {
62} 69}
63 70
64void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 71void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
65 PushCommand(SubmitListCommand(std::move(entries))); 72 const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))};
73 const s64 synchronization_ticks{Core::Timing::usToCycles(9000)};
74 system.CoreTiming().ScheduleEvent(synchronization_ticks, synchronization_event, fence);
66} 75}
67 76
68void ThreadManager::SwapBuffers( 77void ThreadManager::SwapBuffers(
69 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { 78 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
70 state.IncrementFramesCounter();
71 PushCommand(SwapBuffersCommand(std::move(framebuffer))); 79 PushCommand(SwapBuffersCommand(std::move(framebuffer)));
72 state.WaitForFrames();
73} 80}
74 81
75void ThreadManager::FlushRegion(CacheAddr addr, u64 size) { 82void ThreadManager::FlushRegion(CacheAddr addr, u64 size) {
@@ -79,7 +86,7 @@ void ThreadManager::FlushRegion(CacheAddr addr, u64 size) {
79void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) { 86void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) {
80 if (state.queue.Empty()) { 87 if (state.queue.Empty()) {
81 // It's quicker to invalidate a single region on the CPU if the queue is already empty 88 // It's quicker to invalidate a single region on the CPU if the queue is already empty
82 renderer.Rasterizer().InvalidateRegion(addr, size); 89 system.Renderer().Rasterizer().InvalidateRegion(addr, size);
83 } else { 90 } else {
84 PushCommand(InvalidateRegionCommand(addr, size)); 91 PushCommand(InvalidateRegionCommand(addr, size));
85 } 92 }
@@ -90,9 +97,25 @@ void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
90 InvalidateRegion(addr, size); 97 InvalidateRegion(addr, size);
91} 98}
92 99
93void ThreadManager::PushCommand(CommandData&& command_data) { 100u64 ThreadManager::PushCommand(CommandData&& command_data) {
94 state.queue.Push(CommandDataContainer(std::move(command_data))); 101 const u64 fence{++state.last_fence};
102 state.queue.Push(CommandDataContainer(std::move(command_data), fence));
95 state.SignalCommands(); 103 state.SignalCommands();
104 return fence;
105}
106
107MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
108void SynchState::WaitForSynchronization(u64 fence) {
109 if (signaled_fence >= fence) {
110 return;
111 }
112
113 // Wait for the GPU to be idle (all commands to be executed)
114 {
115 MICROPROFILE_SCOPE(GPU_wait);
116 std::unique_lock<std::mutex> lock{synchronization_mutex};
117 synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; });
118 }
96} 119}
97 120
98} // namespace VideoCommon::GPUThread 121} // namespace VideoCommon::GPUThread
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 70acb2e79..62bcea5bb 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -19,9 +19,12 @@ struct FramebufferConfig;
19class DmaPusher; 19class DmaPusher;
20} // namespace Tegra 20} // namespace Tegra
21 21
22namespace VideoCore { 22namespace Core {
23class RendererBase; 23class System;
24} // namespace VideoCore 24namespace Timing {
25struct EventType;
26} // namespace Timing
27} // namespace Core
25 28
26namespace VideoCommon::GPUThread { 29namespace VideoCommon::GPUThread {
27 30
@@ -75,63 +78,47 @@ using CommandData =
75struct CommandDataContainer { 78struct CommandDataContainer {
76 CommandDataContainer() = default; 79 CommandDataContainer() = default;
77 80
78 CommandDataContainer(CommandData&& data) : data{std::move(data)} {} 81 CommandDataContainer(CommandData&& data, u64 next_fence)
82 : data{std::move(data)}, fence{next_fence} {}
79 83
80 CommandDataContainer& operator=(const CommandDataContainer& t) { 84 CommandDataContainer& operator=(const CommandDataContainer& t) {
81 data = std::move(t.data); 85 data = std::move(t.data);
86 fence = t.fence;
82 return *this; 87 return *this;
83 } 88 }
84 89
85 CommandData data; 90 CommandData data;
91 u64 fence{};
86}; 92};
87 93
88/// Struct used to synchronize the GPU thread 94/// Struct used to synchronize the GPU thread
89struct SynchState final { 95struct SynchState final {
90 std::atomic_bool is_running{true}; 96 std::atomic_bool is_running{true};
91 std::atomic_int queued_frame_count{}; 97 std::atomic_int queued_frame_count{};
92 std::mutex frames_mutex; 98 std::mutex synchronization_mutex;
93 std::mutex commands_mutex; 99 std::mutex commands_mutex;
94 std::condition_variable commands_condition; 100 std::condition_variable commands_condition;
95 std::condition_variable frames_condition; 101 std::condition_variable synchronization_condition;
96 102
97 void IncrementFramesCounter() { 103 /// Returns true if the gap in GPU commands is small enough that we can consider the CPU and GPU
98 std::lock_guard lock{frames_mutex}; 104 /// synchronized. This is entirely empirical.
99 ++queued_frame_count; 105 bool IsSynchronized() const {
106 constexpr std::size_t max_queue_gap{5};
107 return queue.Size() <= max_queue_gap;
100 } 108 }
101 109
102 void DecrementFramesCounter() { 110 void TrySynchronize() {
103 { 111 if (IsSynchronized()) {
104 std::lock_guard lock{frames_mutex}; 112 std::lock_guard<std::mutex> lock{synchronization_mutex};
105 --queued_frame_count; 113 synchronization_condition.notify_one();
106
107 if (queued_frame_count) {
108 return;
109 }
110 } 114 }
111 frames_condition.notify_one();
112 } 115 }
113 116
114 void WaitForFrames() { 117 void WaitForSynchronization(u64 fence);
115 {
116 std::lock_guard lock{frames_mutex};
117 if (!queued_frame_count) {
118 return;
119 }
120 }
121
122 // Wait for the GPU to be idle (all commands to be executed)
123 {
124 std::unique_lock lock{frames_mutex};
125 frames_condition.wait(lock, [this] { return !queued_frame_count; });
126 }
127 }
128 118
129 void SignalCommands() { 119 void SignalCommands() {
130 { 120 if (queue.Empty()) {
131 std::unique_lock lock{commands_mutex}; 121 return;
132 if (queue.Empty()) {
133 return;
134 }
135 } 122 }
136 123
137 commands_condition.notify_one(); 124 commands_condition.notify_one();
@@ -144,12 +131,15 @@ struct SynchState final {
144 131
145 using CommandQueue = Common::SPSCQueue<CommandDataContainer>; 132 using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
146 CommandQueue queue; 133 CommandQueue queue;
134 u64 last_fence{};
135 std::atomic<u64> signaled_fence{};
147}; 136};
148 137
149/// Class used to manage the GPU thread 138/// Class used to manage the GPU thread
150class ThreadManager final { 139class ThreadManager final {
151public: 140public:
152 explicit ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher); 141 explicit ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
142 Tegra::DmaPusher& dma_pusher);
153 ~ThreadManager(); 143 ~ThreadManager();
154 144
155 /// Push GPU command entries to be processed 145 /// Push GPU command entries to be processed
@@ -170,11 +160,12 @@ public:
170 160
171private: 161private:
172 /// Pushes a command to be executed by the GPU thread 162 /// Pushes a command to be executed by the GPU thread
173 void PushCommand(CommandData&& command_data); 163 u64 PushCommand(CommandData&& command_data);
174 164
175private: 165private:
176 SynchState state; 166 SynchState state;
177 VideoCore::RendererBase& renderer; 167 Core::System& system;
168 Core::Timing::EventType* synchronization_event{};
178 std::thread thread; 169 std::thread thread;
179 std::thread::id thread_id; 170 std::thread::id thread_id;
180}; 171};
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 64f75db43..524d9ea5a 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -223,27 +223,21 @@ void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 res
223} 223}
224 224
225u32 MacroInterpreter::FetchParameter() { 225u32 MacroInterpreter::FetchParameter() {
226 ASSERT(next_parameter_index < parameters.size()); 226 return parameters.at(next_parameter_index++);
227 return parameters[next_parameter_index++];
228} 227}
229 228
230u32 MacroInterpreter::GetRegister(u32 register_id) const { 229u32 MacroInterpreter::GetRegister(u32 register_id) const {
231 // Register 0 is supposed to always return 0. 230 return registers.at(register_id);
232 if (register_id == 0)
233 return 0;
234
235 ASSERT(register_id < registers.size());
236 return registers[register_id];
237} 231}
238 232
239void MacroInterpreter::SetRegister(u32 register_id, u32 value) { 233void MacroInterpreter::SetRegister(u32 register_id, u32 value) {
240 // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero 234 // Register 0 is hardwired as the zero register.
241 // register. 235 // Ensure no writes to it actually occur.
242 if (register_id == 0) 236 if (register_id == 0) {
243 return; 237 return;
238 }
244 239
245 ASSERT(register_id < registers.size()); 240 registers.at(register_id) = value;
246 registers[register_id] = value;
247} 241}
248 242
249void MacroInterpreter::SetMethodAddress(u32 address) { 243void MacroInterpreter::SetMethodAddress(u32 address) {
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index e76b59842..0f4e820aa 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -5,16 +5,13 @@
5#include "common/alignment.h" 5#include "common/alignment.h"
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h"
9#include "core/memory.h" 8#include "core/memory.h"
10#include "video_core/gpu.h"
11#include "video_core/memory_manager.h" 9#include "video_core/memory_manager.h"
12#include "video_core/rasterizer_interface.h" 10#include "video_core/rasterizer_interface.h"
13#include "video_core/renderer_base.h"
14 11
15namespace Tegra { 12namespace Tegra {
16 13
17MemoryManager::MemoryManager() { 14MemoryManager::MemoryManager(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {
18 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); 15 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
19 std::fill(page_table.attributes.begin(), page_table.attributes.end(), 16 std::fill(page_table.attributes.begin(), page_table.attributes.end(),
20 Common::PageType::Unmapped); 17 Common::PageType::Unmapped);
@@ -70,23 +67,23 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
70 const u64 aligned_size{Common::AlignUp(size, page_size)}; 67 const u64 aligned_size{Common::AlignUp(size, page_size)};
71 const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))}; 68 const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))};
72 69
73 Core::System::GetInstance().Renderer().Rasterizer().FlushAndInvalidateRegion(cache_addr, 70 rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size);
74 aligned_size);
75 UnmapRange(gpu_addr, aligned_size); 71 UnmapRange(gpu_addr, aligned_size);
76 72
77 return gpu_addr; 73 return gpu_addr;
78} 74}
79 75
80GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) { 76GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) const {
81 // Find the first Free VMA. 77 // Find the first Free VMA.
82 const VMAHandle vma_handle{std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { 78 const VMAHandle vma_handle{
83 if (vma.second.type != VirtualMemoryArea::Type::Unmapped) { 79 std::find_if(vma_map.begin(), vma_map.end(), [region_start, size](const auto& vma) {
84 return false; 80 if (vma.second.type != VirtualMemoryArea::Type::Unmapped) {
85 } 81 return false;
82 }
86 83
87 const VAddr vma_end{vma.second.base + vma.second.size}; 84 const VAddr vma_end{vma.second.base + vma.second.size};
88 return vma_end > region_start && vma_end >= region_start + size; 85 return vma_end > region_start && vma_end >= region_start + size;
89 })}; 86 })};
90 87
91 if (vma_handle == vma_map.end()) { 88 if (vma_handle == vma_map.end()) {
92 return {}; 89 return {};
@@ -99,12 +96,12 @@ bool MemoryManager::IsAddressValid(GPUVAddr addr) const {
99 return (addr >> page_bits) < page_table.pointers.size(); 96 return (addr >> page_bits) < page_table.pointers.size();
100} 97}
101 98
102std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) { 99std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) const {
103 if (!IsAddressValid(addr)) { 100 if (!IsAddressValid(addr)) {
104 return {}; 101 return {};
105 } 102 }
106 103
107 VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]}; 104 const VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]};
108 if (cpu_addr) { 105 if (cpu_addr) {
109 return cpu_addr + (addr & page_mask); 106 return cpu_addr + (addr & page_mask);
110 } 107 }
@@ -113,7 +110,7 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) {
113} 110}
114 111
115template <typename T> 112template <typename T>
116T MemoryManager::Read(GPUVAddr addr) { 113T MemoryManager::Read(GPUVAddr addr) const {
117 if (!IsAddressValid(addr)) { 114 if (!IsAddressValid(addr)) {
118 return {}; 115 return {};
119 } 116 }
@@ -165,10 +162,10 @@ void MemoryManager::Write(GPUVAddr addr, T data) {
165 } 162 }
166} 163}
167 164
168template u8 MemoryManager::Read<u8>(GPUVAddr addr); 165template u8 MemoryManager::Read<u8>(GPUVAddr addr) const;
169template u16 MemoryManager::Read<u16>(GPUVAddr addr); 166template u16 MemoryManager::Read<u16>(GPUVAddr addr) const;
170template u32 MemoryManager::Read<u32>(GPUVAddr addr); 167template u32 MemoryManager::Read<u32>(GPUVAddr addr) const;
171template u64 MemoryManager::Read<u64>(GPUVAddr addr); 168template u64 MemoryManager::Read<u64>(GPUVAddr addr) const;
172template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data); 169template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data);
173template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data); 170template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
174template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data); 171template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
@@ -179,8 +176,22 @@ u8* MemoryManager::GetPointer(GPUVAddr addr) {
179 return {}; 176 return {};
180 } 177 }
181 178
182 u8* page_pointer{page_table.pointers[addr >> page_bits]}; 179 u8* const page_pointer{page_table.pointers[addr >> page_bits]};
183 if (page_pointer) { 180 if (page_pointer != nullptr) {
181 return page_pointer + (addr & page_mask);
182 }
183
184 LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr);
185 return {};
186}
187
188const u8* MemoryManager::GetPointer(GPUVAddr addr) const {
189 if (!IsAddressValid(addr)) {
190 return {};
191 }
192
193 const u8* const page_pointer{page_table.pointers[addr >> page_bits]};
194 if (page_pointer != nullptr) {
184 return page_pointer + (addr & page_mask); 195 return page_pointer + (addr & page_mask);
185 } 196 }
186 197
@@ -188,15 +199,86 @@ u8* MemoryManager::GetPointer(GPUVAddr addr) {
188 return {}; 199 return {};
189} 200}
190 201
191void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) { 202void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const {
192 std::memcpy(dest_buffer, GetPointer(src_addr), size); 203 std::size_t remaining_size{size};
204 std::size_t page_index{src_addr >> page_bits};
205 std::size_t page_offset{src_addr & page_mask};
206
207 while (remaining_size > 0) {
208 const std::size_t copy_amount{
209 std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
210
211 switch (page_table.attributes[page_index]) {
212 case Common::PageType::Memory: {
213 const u8* src_ptr{page_table.pointers[page_index] + page_offset};
214 rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount);
215 std::memcpy(dest_buffer, src_ptr, copy_amount);
216 break;
217 }
218 default:
219 UNREACHABLE();
220 }
221
222 page_index++;
223 page_offset = 0;
224 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
225 remaining_size -= copy_amount;
226 }
193} 227}
228
194void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) { 229void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) {
195 std::memcpy(GetPointer(dest_addr), src_buffer, size); 230 std::size_t remaining_size{size};
231 std::size_t page_index{dest_addr >> page_bits};
232 std::size_t page_offset{dest_addr & page_mask};
233
234 while (remaining_size > 0) {
235 const std::size_t copy_amount{
236 std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
237
238 switch (page_table.attributes[page_index]) {
239 case Common::PageType::Memory: {
240 u8* dest_ptr{page_table.pointers[page_index] + page_offset};
241 rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount);
242 std::memcpy(dest_ptr, src_buffer, copy_amount);
243 break;
244 }
245 default:
246 UNREACHABLE();
247 }
248
249 page_index++;
250 page_offset = 0;
251 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
252 remaining_size -= copy_amount;
253 }
196} 254}
197 255
198void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) { 256void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) {
199 std::memcpy(GetPointer(dest_addr), GetPointer(src_addr), size); 257 std::size_t remaining_size{size};
258 std::size_t page_index{src_addr >> page_bits};
259 std::size_t page_offset{src_addr & page_mask};
260
261 while (remaining_size > 0) {
262 const std::size_t copy_amount{
263 std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
264
265 switch (page_table.attributes[page_index]) {
266 case Common::PageType::Memory: {
267 const u8* src_ptr{page_table.pointers[page_index] + page_offset};
268 rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount);
269 WriteBlock(dest_addr, src_ptr, copy_amount);
270 break;
271 }
272 default:
273 UNREACHABLE();
274 }
275
276 page_index++;
277 page_offset = 0;
278 dest_addr += static_cast<VAddr>(copy_amount);
279 src_addr += static_cast<VAddr>(copy_amount);
280 remaining_size -= copy_amount;
281 }
200} 282}
201 283
202void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, 284void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type,
@@ -336,7 +418,7 @@ MemoryManager::VMAIter MemoryManager::CarveVMA(GPUVAddr base, u64 size) {
336 const VirtualMemoryArea& vma{vma_handle->second}; 418 const VirtualMemoryArea& vma{vma_handle->second};
337 if (vma.type == VirtualMemoryArea::Type::Mapped) { 419 if (vma.type == VirtualMemoryArea::Type::Mapped) {
338 // Region is already allocated 420 // Region is already allocated
339 return {}; 421 return vma_handle;
340 } 422 }
341 423
342 const VAddr start_in_vma{base - vma.base}; 424 const VAddr start_in_vma{base - vma.base};
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 34744bb27..647cbf93a 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -10,6 +10,10 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/page_table.h" 11#include "common/page_table.h"
12 12
13namespace VideoCore {
14class RasterizerInterface;
15}
16
13namespace Tegra { 17namespace Tegra {
14 18
15/** 19/**
@@ -43,24 +47,25 @@ struct VirtualMemoryArea {
43 47
44class MemoryManager final { 48class MemoryManager final {
45public: 49public:
46 MemoryManager(); 50 MemoryManager(VideoCore::RasterizerInterface& rasterizer);
47 51
48 GPUVAddr AllocateSpace(u64 size, u64 align); 52 GPUVAddr AllocateSpace(u64 size, u64 align);
49 GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align); 53 GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align);
50 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); 54 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
51 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size); 55 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size);
52 GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size); 56 GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size);
53 std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr); 57 std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
54 58
55 template <typename T> 59 template <typename T>
56 T Read(GPUVAddr addr); 60 T Read(GPUVAddr addr) const;
57 61
58 template <typename T> 62 template <typename T>
59 void Write(GPUVAddr addr, T data); 63 void Write(GPUVAddr addr, T data);
60 64
61 u8* GetPointer(GPUVAddr addr); 65 u8* GetPointer(GPUVAddr addr);
66 const u8* GetPointer(GPUVAddr addr) const;
62 67
63 void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size); 68 void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const;
64 void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); 69 void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size);
65 void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); 70 void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size);
66 71
@@ -127,7 +132,7 @@ private:
127 void UpdatePageTableForVMA(const VirtualMemoryArea& vma); 132 void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
128 133
129 /// Finds a free (unmapped region) of the specified size starting at the specified address. 134 /// Finds a free (unmapped region) of the specified size starting at the specified address.
130 GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size); 135 GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size) const;
131 136
132private: 137private:
133 static constexpr u64 page_bits{16}; 138 static constexpr u64 page_bits{16};
@@ -143,6 +148,7 @@ private:
143 148
144 Common::PageTable page_table{page_bits}; 149 Common::PageTable page_table{page_bits};
145 VMAMap vma_map; 150 VMAMap vma_map;
151 VideoCore::RasterizerInterface& rasterizer;
146}; 152};
147 153
148} // namespace Tegra 154} // namespace Tegra
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index fd091c84c..25652e794 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -7,7 +7,7 @@
7 7
8#include "common/alignment.h" 8#include "common/alignment.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/memory.h" 10#include "video_core/memory_manager.h"
11#include "video_core/renderer_opengl/gl_buffer_cache.h" 11#include "video_core/renderer_opengl/gl_buffer_cache.h"
12#include "video_core/renderer_opengl/gl_rasterizer.h" 12#include "video_core/renderer_opengl/gl_rasterizer.h"
13 13
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp
index da9326253..ea4a593af 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_global_cache.cpp
@@ -4,9 +4,9 @@
4 4
5#include <glad/glad.h> 5#include <glad/glad.h>
6 6
7#include "common/assert.h"
8#include "common/logging/log.h" 7#include "common/logging/log.h"
9#include "core/core.h" 8#include "core/core.h"
9#include "video_core/memory_manager.h"
10#include "video_core/renderer_opengl/gl_global_cache.h" 10#include "video_core/renderer_opengl/gl_global_cache.h"
11#include "video_core/renderer_opengl/gl_rasterizer.h" 11#include "video_core/renderer_opengl/gl_rasterizer.h"
12#include "video_core/renderer_opengl/gl_shader_decompiler.h" 12#include "video_core/renderer_opengl/gl_shader_decompiler.h"
@@ -14,28 +14,28 @@
14 14
15namespace OpenGL { 15namespace OpenGL {
16 16
17CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr) 17CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u8* host_ptr, u32 size, u32 max_size)
18 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size} { 18 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, host_ptr{host_ptr}, size{size},
19 max_size{max_size} {
19 buffer.Create(); 20 buffer.Create();
20 // Bind and unbind the buffer so it gets allocated by the driver
21 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
22 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
23 LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory"); 21 LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory");
24} 22}
25 23
26void CachedGlobalRegion::Reload(u32 size_) { 24CachedGlobalRegion::~CachedGlobalRegion() = default;
27 constexpr auto max_size = static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize);
28 25
26void CachedGlobalRegion::Reload(u32 size_) {
29 size = size_; 27 size = size_;
30 if (size > max_size) { 28 if (size > max_size) {
31 size = max_size; 29 size = max_size;
32 LOG_CRITICAL(HW_GPU, "Global region size {} exceeded the expected size {}!", size_, 30 LOG_CRITICAL(HW_GPU, "Global region size {} exceeded the supported size {}!", size_,
33 max_size); 31 max_size);
34 } 32 }
33 glNamedBufferData(buffer.handle, size, host_ptr, GL_STREAM_DRAW);
34}
35 35
36 // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer 36void CachedGlobalRegion::Flush() {
37 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); 37 LOG_DEBUG(Render_OpenGL, "Flushing {} bytes to CPU memory address 0x{:16}", size, cpu_addr);
38 glBufferData(GL_SHADER_STORAGE_BUFFER, size, GetHostPtr(), GL_DYNAMIC_DRAW); 38 glGetNamedBufferSubData(buffer.handle, 0, static_cast<GLsizeiptr>(size), host_ptr);
39} 39}
40 40
41GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const { 41GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const {
@@ -46,14 +46,16 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr,
46 return search->second; 46 return search->second;
47} 47}
48 48
49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u32 size, 49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr,
50 u8* host_ptr) { 50 u32 size) {
51 GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)}; 51 GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)};
52 if (!region) { 52 if (!region) {
53 // No reserved surface available, create a new one and reserve it 53 // No reserved surface available, create a new one and reserve it
54 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; 54 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
55 const auto cpu_addr = *memory_manager.GpuToCpuAddress(addr); 55 const auto cpu_addr{memory_manager.GpuToCpuAddress(addr)};
56 region = std::make_shared<CachedGlobalRegion>(cpu_addr, size, host_ptr); 56 ASSERT(cpu_addr);
57
58 region = std::make_shared<CachedGlobalRegion>(*cpu_addr, host_ptr, size, max_ssbo_size);
57 ReserveGlobalRegion(region); 59 ReserveGlobalRegion(region);
58 } 60 }
59 region->Reload(size); 61 region->Reload(size);
@@ -65,7 +67,11 @@ void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) {
65} 67}
66 68
67GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer) 69GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
68 : RasterizerCache{rasterizer} {} 70 : RasterizerCache{rasterizer} {
71 GLint max_ssbo_size_;
72 glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &max_ssbo_size_);
73 max_ssbo_size = static_cast<u32>(max_ssbo_size_);
74}
69 75
70GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( 76GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
71 const GLShader::GlobalMemoryEntry& global_region, 77 const GLShader::GlobalMemoryEntry& global_region,
@@ -73,7 +79,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
73 79
74 auto& gpu{Core::System::GetInstance().GPU()}; 80 auto& gpu{Core::System::GetInstance().GPU()};
75 auto& memory_manager{gpu.MemoryManager()}; 81 auto& memory_manager{gpu.MemoryManager()};
76 const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]}; 82 const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<std::size_t>(stage)]};
77 const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address + 83 const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address +
78 global_region.GetCbufOffset()}; 84 global_region.GetCbufOffset()};
79 const auto actual_addr{memory_manager.Read<u64>(addr)}; 85 const auto actual_addr{memory_manager.Read<u64>(addr)};
@@ -85,7 +91,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
85 91
86 if (!region) { 92 if (!region) {
87 // No global region found - create a new one 93 // No global region found - create a new one
88 region = GetUncachedGlobalRegion(actual_addr, size, host_ptr); 94 region = GetUncachedGlobalRegion(actual_addr, host_ptr, size);
89 Register(region); 95 Register(region);
90 } 96 }
91 97
diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h
index 5a21ab66f..196e6e278 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.h
+++ b/src/video_core/renderer_opengl/gl_global_cache.h
@@ -19,7 +19,7 @@ namespace OpenGL {
19 19
20namespace GLShader { 20namespace GLShader {
21class GlobalMemoryEntry; 21class GlobalMemoryEntry;
22} // namespace GLShader 22}
23 23
24class RasterizerOpenGL; 24class RasterizerOpenGL;
25class CachedGlobalRegion; 25class CachedGlobalRegion;
@@ -27,7 +27,8 @@ using GlobalRegion = std::shared_ptr<CachedGlobalRegion>;
27 27
28class CachedGlobalRegion final : public RasterizerCacheObject { 28class CachedGlobalRegion final : public RasterizerCacheObject {
29public: 29public:
30 explicit CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr); 30 explicit CachedGlobalRegion(VAddr cpu_addr, u8* host_ptr, u32 size, u32 max_size);
31 ~CachedGlobalRegion();
31 32
32 VAddr GetCpuAddr() const override { 33 VAddr GetCpuAddr() const override {
33 return cpu_addr; 34 return cpu_addr;
@@ -45,14 +46,14 @@ public:
45 /// Reloads the global region from guest memory 46 /// Reloads the global region from guest memory
46 void Reload(u32 size_); 47 void Reload(u32 size_);
47 48
48 // TODO(Rodrigo): When global memory is written (STG), implement flushing 49 void Flush() override;
49 void Flush() override {
50 UNIMPLEMENTED();
51 }
52 50
53private: 51private:
54 VAddr cpu_addr{}; 52 VAddr cpu_addr{};
53 u8* host_ptr{};
55 u32 size{}; 54 u32 size{};
55 u32 max_size{};
56
56 OGLBuffer buffer; 57 OGLBuffer buffer;
57}; 58};
58 59
@@ -66,10 +67,11 @@ public:
66 67
67private: 68private:
68 GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const; 69 GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const;
69 GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u32 size, u8* host_ptr); 70 GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr, u32 size);
70 void ReserveGlobalRegion(GlobalRegion region); 71 void ReserveGlobalRegion(GlobalRegion region);
71 72
72 std::unordered_map<CacheAddr, GlobalRegion> reserve; 73 std::unordered_map<CacheAddr, GlobalRegion> reserve;
74 u32 max_ssbo_size{};
73}; 75};
74 76
75} // namespace OpenGL 77} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
index 2bcbd3da2..c3e94d917 100644
--- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
+++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
@@ -7,7 +7,7 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/memory.h" 10#include "video_core/memory_manager.h"
11#include "video_core/renderer_opengl/gl_buffer_cache.h" 11#include "video_core/renderer_opengl/gl_buffer_cache.h"
12#include "video_core/renderer_opengl/gl_primitive_assembler.h" 12#include "video_core/renderer_opengl/gl_primitive_assembler.h"
13 13
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.h b/src/video_core/renderer_opengl/gl_primitive_assembler.h
index 0e2e7dc36..4e87ce4d6 100644
--- a/src/video_core/renderer_opengl/gl_primitive_assembler.h
+++ b/src/video_core/renderer_opengl/gl_primitive_assembler.h
@@ -4,11 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector>
8#include <glad/glad.h> 7#include <glad/glad.h>
9 8
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "video_core/memory_manager.h"
12 10
13namespace OpenGL { 11namespace OpenGL {
14 12
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 046fc935b..0b1fe3494 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -17,7 +17,6 @@
17#include "common/microprofile.h" 17#include "common/microprofile.h"
18#include "common/scope_exit.h" 18#include "common/scope_exit.h"
19#include "core/core.h" 19#include "core/core.h"
20#include "core/frontend/emu_window.h"
21#include "core/hle/kernel/process.h" 20#include "core/hle/kernel/process.h"
22#include "core/settings.h" 21#include "core/settings.h"
23#include "video_core/engines/maxwell_3d.h" 22#include "video_core/engines/maxwell_3d.h"
@@ -26,7 +25,6 @@
26#include "video_core/renderer_opengl/gl_shader_gen.h" 25#include "video_core/renderer_opengl/gl_shader_gen.h"
27#include "video_core/renderer_opengl/maxwell_to_gl.h" 26#include "video_core/renderer_opengl/maxwell_to_gl.h"
28#include "video_core/renderer_opengl/renderer_opengl.h" 27#include "video_core/renderer_opengl/renderer_opengl.h"
29#include "video_core/video_core.h"
30 28
31namespace OpenGL { 29namespace OpenGL {
32 30
@@ -301,6 +299,10 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
301 BaseBindings base_bindings; 299 BaseBindings base_bindings;
302 std::array<bool, Maxwell::NumClipDistances> clip_distances{}; 300 std::array<bool, Maxwell::NumClipDistances> clip_distances{};
303 301
302 // Prepare packed bindings
303 bind_ubo_pushbuffer.Setup(base_bindings.cbuf);
304 bind_ssbo_pushbuffer.Setup(base_bindings.gmem);
305
304 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 306 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
305 const auto& shader_config = gpu.regs.shader_config[index]; 307 const auto& shader_config = gpu.regs.shader_config[index];
306 const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; 308 const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)};
@@ -318,13 +320,13 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
318 const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 320 const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
319 321
320 GLShader::MaxwellUniformData ubo{}; 322 GLShader::MaxwellUniformData ubo{};
321 ubo.SetFromRegs(gpu.state.shader_stages[stage]); 323 ubo.SetFromRegs(gpu, stage);
322 const GLintptr offset = buffer_cache.UploadHostMemory( 324 const GLintptr offset = buffer_cache.UploadHostMemory(
323 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); 325 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
324 326
325 // Bind the emulation info buffer 327 // Bind the emulation info buffer
326 glBindBufferRange(GL_UNIFORM_BUFFER, base_bindings.cbuf, buffer_cache.GetHandle(), offset, 328 bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset,
327 static_cast<GLsizeiptr>(sizeof(ubo))); 329 static_cast<GLsizeiptr>(sizeof(ubo)));
328 330
329 Shader shader{shader_cache.GetStageProgram(program)}; 331 Shader shader{shader_cache.GetStageProgram(program)};
330 const auto [program_handle, next_bindings] = 332 const auto [program_handle, next_bindings] =
@@ -368,6 +370,9 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
368 base_bindings = next_bindings; 370 base_bindings = next_bindings;
369 } 371 }
370 372
373 bind_ubo_pushbuffer.Bind();
374 bind_ssbo_pushbuffer.Bind();
375
371 SyncClipEnabled(clip_distances); 376 SyncClipEnabled(clip_distances);
372 377
373 gpu.dirty_flags.shaders = false; 378 gpu.dirty_flags.shaders = false;
@@ -577,9 +582,6 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
577} 582}
578 583
579void RasterizerOpenGL::Clear() { 584void RasterizerOpenGL::Clear() {
580 const auto prev_state{state};
581 SCOPE_EXIT({ prev_state.Apply(); });
582
583 const auto& regs = system.GPU().Maxwell3D().regs; 585 const auto& regs = system.GPU().Maxwell3D().regs;
584 bool use_color{}; 586 bool use_color{};
585 bool use_depth{}; 587 bool use_depth{};
@@ -651,7 +653,10 @@ void RasterizerOpenGL::Clear() {
651 clear_state.EmulateViewportWithScissor(); 653 clear_state.EmulateViewportWithScissor();
652 } 654 }
653 655
654 clear_state.Apply(); 656 clear_state.ApplyColorMask();
657 clear_state.ApplyDepth();
658 clear_state.ApplyStencilTest();
659 clear_state.ApplyViewport();
655 660
656 if (use_color) { 661 if (use_color) {
657 glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color); 662 glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color);
@@ -751,6 +756,7 @@ void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) {
751 return; 756 return;
752 } 757 }
753 res_cache.FlushRegion(addr, size); 758 res_cache.FlushRegion(addr, size);
759 global_cache.FlushRegion(addr, size);
754} 760}
755 761
756void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) { 762void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) {
@@ -902,23 +908,14 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader
902 const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)]; 908 const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)];
903 const auto& entries = shader->GetShaderEntries().const_buffers; 909 const auto& entries = shader->GetShaderEntries().const_buffers;
904 910
905 constexpr u64 max_binds = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers;
906 std::array<GLuint, max_binds> bind_buffers;
907 std::array<GLintptr, max_binds> bind_offsets;
908 std::array<GLsizeiptr, max_binds> bind_sizes;
909
910 ASSERT_MSG(entries.size() <= max_binds, "Exceeded expected number of binding points.");
911
912 // Upload only the enabled buffers from the 16 constbuffers of each shader stage 911 // Upload only the enabled buffers from the 16 constbuffers of each shader stage
913 for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { 912 for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
914 const auto& used_buffer = entries[bindpoint]; 913 const auto& used_buffer = entries[bindpoint];
915 const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; 914 const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()];
916 915
917 if (!buffer.enabled) { 916 if (!buffer.enabled) {
918 // With disabled buffers set values as zero to unbind them 917 // Set values to zero to unbind buffers
919 bind_buffers[bindpoint] = 0; 918 bind_ubo_pushbuffer.Push(0, 0, 0);
920 bind_offsets[bindpoint] = 0;
921 bind_sizes[bindpoint] = 0;
922 continue; 919 continue;
923 } 920 }
924 921
@@ -946,30 +943,22 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader
946 const GLintptr const_buffer_offset = buffer_cache.UploadMemory( 943 const GLintptr const_buffer_offset = buffer_cache.UploadMemory(
947 buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); 944 buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
948 945
949 // Prepare values for multibind 946 bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size);
950 bind_buffers[bindpoint] = buffer_cache.GetHandle();
951 bind_offsets[bindpoint] = const_buffer_offset;
952 bind_sizes[bindpoint] = size;
953 } 947 }
954
955 // The first binding is reserved for emulation values
956 const GLuint ubo_base_binding = base_bindings.cbuf + 1;
957 glBindBuffersRange(GL_UNIFORM_BUFFER, ubo_base_binding, static_cast<GLsizei>(entries.size()),
958 bind_buffers.data(), bind_offsets.data(), bind_sizes.data());
959} 948}
960 949
961void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, 950void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
962 const Shader& shader, GLenum primitive_mode, 951 const Shader& shader, GLenum primitive_mode,
963 BaseBindings base_bindings) { 952 BaseBindings base_bindings) {
964 // TODO(Rodrigo): Use ARB_multi_bind here
965 const auto& entries = shader->GetShaderEntries().global_memory_entries; 953 const auto& entries = shader->GetShaderEntries().global_memory_entries;
966 954 for (std::size_t bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
967 for (u32 bindpoint = 0; bindpoint < static_cast<u32>(entries.size()); ++bindpoint) { 955 const auto& entry{entries[bindpoint]};
968 const auto& entry = entries[bindpoint]; 956 const auto& region{global_cache.GetGlobalRegion(entry, stage)};
969 const u32 current_bindpoint = base_bindings.gmem + bindpoint; 957 if (entry.IsWritten()) {
970 const auto& region = global_cache.GetGlobalRegion(entry, stage); 958 region->MarkAsModified(true, global_cache);
971 959 }
972 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, current_bindpoint, region->GetBufferHandle()); 960 bind_ssbo_pushbuffer.Push(region->GetBufferHandle(), 0,
961 static_cast<GLsizeiptr>(region->GetSizeInBytes()));
973 } 962 }
974} 963}
975 964
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 4de565321..d4c2cf80e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -12,15 +12,12 @@
12#include <optional> 12#include <optional>
13#include <tuple> 13#include <tuple>
14#include <utility> 14#include <utility>
15#include <vector>
16 15
17#include <boost/icl/interval_map.hpp> 16#include <boost/icl/interval_map.hpp>
18#include <boost/range/iterator_range.hpp>
19#include <glad/glad.h> 17#include <glad/glad.h>
20 18
21#include "common/common_types.h" 19#include "common/common_types.h"
22#include "video_core/engines/maxwell_3d.h" 20#include "video_core/engines/maxwell_3d.h"
23#include "video_core/memory_manager.h"
24#include "video_core/rasterizer_cache.h" 21#include "video_core/rasterizer_cache.h"
25#include "video_core/rasterizer_interface.h" 22#include "video_core/rasterizer_interface.h"
26#include "video_core/renderer_opengl/gl_buffer_cache.h" 23#include "video_core/renderer_opengl/gl_buffer_cache.h"
@@ -29,10 +26,9 @@
29#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 26#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
30#include "video_core/renderer_opengl/gl_resource_manager.h" 27#include "video_core/renderer_opengl/gl_resource_manager.h"
31#include "video_core/renderer_opengl/gl_shader_cache.h" 28#include "video_core/renderer_opengl/gl_shader_cache.h"
32#include "video_core/renderer_opengl/gl_shader_gen.h"
33#include "video_core/renderer_opengl/gl_shader_manager.h" 29#include "video_core/renderer_opengl/gl_shader_manager.h"
34#include "video_core/renderer_opengl/gl_state.h" 30#include "video_core/renderer_opengl/gl_state.h"
35#include "video_core/renderer_opengl/gl_stream_buffer.h" 31#include "video_core/renderer_opengl/utils.h"
36 32
37namespace Core { 33namespace Core {
38class System; 34class System;
@@ -75,10 +71,6 @@ public:
75 static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, 71 static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0,
76 "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); 72 "The maximum size of a constbuffer must be a multiple of the size of GLvec4");
77 73
78 static constexpr std::size_t MaxGlobalMemorySize = 0x10000;
79 static_assert(MaxGlobalMemorySize % sizeof(float) == 0,
80 "The maximum size of a global memory must be a multiple of the size of float");
81
82private: 74private:
83 class SamplerInfo { 75 class SamplerInfo {
84 public: 76 public:
@@ -234,6 +226,9 @@ private:
234 PrimitiveAssembler primitive_assembler{buffer_cache}; 226 PrimitiveAssembler primitive_assembler{buffer_cache};
235 GLint uniform_buffer_alignment; 227 GLint uniform_buffer_alignment;
236 228
229 BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
230 BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
231
237 std::size_t CalculateVertexArraysSize() const; 232 std::size_t CalculateVertexArraysSize() const;
238 233
239 std::size_t CalculateIndexBufferSize() const; 234 std::size_t CalculateIndexBufferSize() const;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index aba6ce731..7a68b8738 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -13,9 +13,9 @@
13#include "common/scope_exit.h" 13#include "common/scope_exit.h"
14#include "core/core.h" 14#include "core/core.h"
15#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
16#include "core/memory.h"
17#include "core/settings.h" 16#include "core/settings.h"
18#include "video_core/engines/maxwell_3d.h" 17#include "video_core/engines/maxwell_3d.h"
18#include "video_core/memory_manager.h"
19#include "video_core/morton.h" 19#include "video_core/morton.h"
20#include "video_core/renderer_opengl/gl_rasterizer.h" 20#include "video_core/renderer_opengl/gl_rasterizer.h"
21#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 21#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
@@ -112,11 +112,26 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
112 params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), 112 params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
113 params.srgb_conversion); 113 params.srgb_conversion);
114 114
115 if (params.pixel_format == PixelFormat::R16U && config.tsc.depth_compare_enabled) { 115 if (config.tsc.depth_compare_enabled) {
116 // Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled, 116 // Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled,
117 // then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also 117 // then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also
118 // causes GetFormatType to properly return 'Depth' below). 118 // causes GetFormatType to properly return 'Depth' below).
119 params.pixel_format = PixelFormat::Z16; 119 if (GetFormatType(params.pixel_format) == SurfaceType::ColorTexture) {
120 switch (params.pixel_format) {
121 case PixelFormat::R16S:
122 case PixelFormat::R16U:
123 case PixelFormat::R16F:
124 params.pixel_format = PixelFormat::Z16;
125 break;
126 case PixelFormat::R32F:
127 params.pixel_format = PixelFormat::Z32F;
128 break;
129 default:
130 LOG_WARNING(HW_GPU, "Color texture format being used with depth compare: {}",
131 static_cast<u32>(params.pixel_format));
132 break;
133 }
134 }
120 } 135 }
121 136
122 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); 137 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
@@ -266,6 +281,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
266 params.component_type = ComponentTypeFromRenderTarget(config.format); 281 params.component_type = ComponentTypeFromRenderTarget(config.format);
267 params.type = GetFormatType(params.pixel_format); 282 params.type = GetFormatType(params.pixel_format);
268 params.width = config.width; 283 params.width = config.width;
284 params.pitch = config.pitch;
269 params.height = config.height; 285 params.height = config.height;
270 params.unaligned_height = config.height; 286 params.unaligned_height = config.height;
271 params.target = SurfaceTarget::Texture2D; 287 params.target = SurfaceTarget::Texture2D;
@@ -662,8 +678,8 @@ void CachedSurface::FlushGLBuffer() {
662 gl_buffer[0].resize(GetSizeInBytes()); 678 gl_buffer[0].resize(GetSizeInBytes());
663 679
664 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 680 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
665 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 681 const u32 align = std::clamp(params.RowAlign(0), 1U, 8U);
666 ASSERT(params.width * GetBytesPerPixel(params.pixel_format) % 4 == 0); 682 glPixelStorei(GL_PACK_ALIGNMENT, align);
667 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); 683 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
668 ASSERT(!tuple.compressed); 684 ASSERT(!tuple.compressed);
669 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 685 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -708,8 +724,8 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
708 724
709 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 725 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
710 726
711 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 727 const u32 align = std::clamp(params.RowAlign(mip_map), 1U, 8U);
712 ASSERT(params.MipWidth(mip_map) * GetBytesPerPixel(params.pixel_format) % 4 == 0); 728 glPixelStorei(GL_UNPACK_ALIGNMENT, align);
713 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map))); 729 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map)));
714 730
715 const auto image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false)); 731 const auto image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false));
@@ -1175,10 +1191,16 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1175 return new_surface; 1191 return new_surface;
1176 } 1192 }
1177 1193
1194 const bool old_compressed =
1195 GetFormatTuple(old_params.pixel_format, old_params.component_type).compressed;
1196 const bool new_compressed =
1197 GetFormatTuple(new_params.pixel_format, new_params.component_type).compressed;
1198 const bool compatible_formats =
1199 GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format) &&
1200 !(old_compressed || new_compressed);
1178 // For compatible surfaces, we can just do fast glCopyImageSubData based copy 1201 // For compatible surfaces, we can just do fast glCopyImageSubData based copy
1179 if (old_params.target == new_params.target && old_params.type == new_params.type && 1202 if (old_params.target == new_params.target && old_params.depth == new_params.depth &&
1180 old_params.depth == new_params.depth && old_params.depth == 1 && 1203 old_params.depth == 1 && compatible_formats) {
1181 GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) {
1182 FastCopySurface(old_surface, new_surface); 1204 FastCopySurface(old_surface, new_surface);
1183 return new_surface; 1205 return new_surface;
1184 } 1206 }
@@ -1193,7 +1215,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1193 case SurfaceTarget::TextureCubemap: 1215 case SurfaceTarget::TextureCubemap:
1194 case SurfaceTarget::Texture2DArray: 1216 case SurfaceTarget::Texture2DArray:
1195 case SurfaceTarget::TextureCubeArray: 1217 case SurfaceTarget::TextureCubeArray:
1196 if (old_params.pixel_format == new_params.pixel_format) 1218 if (compatible_formats)
1197 FastLayeredCopySurface(old_surface, new_surface); 1219 FastLayeredCopySurface(old_surface, new_surface);
1198 else { 1220 else {
1199 AccurateCopySurface(old_surface, new_surface); 1221 AccurateCopySurface(old_surface, new_surface);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index e8073579f..db280dbb3 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -5,13 +5,13 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <map>
9#include <memory> 8#include <memory>
10#include <string> 9#include <string>
11#include <unordered_set> 10#include <tuple>
12#include <vector> 11#include <vector>
13 12
14#include "common/alignment.h" 13#include "common/alignment.h"
14#include "common/bit_util.h"
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "common/hash.h" 16#include "common/hash.h"
17#include "common/math_util.h" 17#include "common/math_util.h"
@@ -206,6 +206,13 @@ struct SurfaceParams {
206 return bd; 206 return bd;
207 } 207 }
208 208
209 u32 RowAlign(u32 mip_level) const {
210 const u32 m_width = MipWidth(mip_level);
211 const u32 bytes_per_pixel = GetBytesPerPixel(pixel_format);
212 const u32 l2 = Common::CountTrailingZeroes32(m_width * bytes_per_pixel);
213 return (1U << l2);
214 }
215
209 /// Creates SurfaceParams from a texture configuration 216 /// Creates SurfaceParams from a texture configuration
210 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, 217 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config,
211 const GLShader::SamplerEntry& entry); 218 const GLShader::SamplerEntry& entry);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 290e654bc..99f67494c 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -6,13 +6,12 @@
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/hash.h" 7#include "common/hash.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/memory.h"
10#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/memory_manager.h"
11#include "video_core/renderer_opengl/gl_rasterizer.h" 11#include "video_core/renderer_opengl/gl_rasterizer.h"
12#include "video_core/renderer_opengl/gl_shader_cache.h" 12#include "video_core/renderer_opengl/gl_shader_cache.h"
13#include "video_core/renderer_opengl/gl_shader_decompiler.h" 13#include "video_core/renderer_opengl/gl_shader_decompiler.h"
14#include "video_core/renderer_opengl/gl_shader_disk_cache.h" 14#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
15#include "video_core/renderer_opengl/gl_shader_manager.h"
16#include "video_core/renderer_opengl/utils.h" 15#include "video_core/renderer_opengl/utils.h"
17#include "video_core/shader/shader_ir.h" 16#include "video_core/shader/shader_ir.h"
18 17
@@ -41,6 +40,10 @@ GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) {
41/// Gets the shader program code from memory for the specified address 40/// Gets the shader program code from memory for the specified address
42ProgramCode GetShaderCode(const u8* host_ptr) { 41ProgramCode GetShaderCode(const u8* host_ptr) {
43 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); 42 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
43 ASSERT_OR_EXECUTE(host_ptr != nullptr, {
44 std::fill(program_code.begin(), program_code.end(), 0);
45 return program_code;
46 });
44 std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64)); 47 std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64));
45 return program_code; 48 return program_code;
46} 49}
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index fd1c85115..0cf8e0b3d 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -5,21 +5,20 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
8#include <memory> 9#include <memory>
9#include <set> 10#include <set>
10#include <tuple> 11#include <tuple>
11#include <unordered_map> 12#include <unordered_map>
13#include <vector>
12 14
13#include <glad/glad.h> 15#include <glad/glad.h>
14 16
15#include "common/assert.h"
16#include "common/common_types.h" 17#include "common/common_types.h"
17#include "video_core/rasterizer_cache.h" 18#include "video_core/rasterizer_cache.h"
18#include "video_core/renderer_base.h"
19#include "video_core/renderer_opengl/gl_resource_manager.h" 19#include "video_core/renderer_opengl/gl_resource_manager.h"
20#include "video_core/renderer_opengl/gl_shader_decompiler.h" 20#include "video_core/renderer_opengl/gl_shader_decompiler.h"
21#include "video_core/renderer_opengl/gl_shader_disk_cache.h" 21#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
22#include "video_core/renderer_opengl/gl_shader_gen.h"
23 22
24namespace Core { 23namespace Core {
25class System; 24class System;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 11d1169f0..445048daf 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -21,6 +21,8 @@
21 21
22namespace OpenGL::GLShader { 22namespace OpenGL::GLShader {
23 23
24namespace {
25
24using Tegra::Shader::Attribute; 26using Tegra::Shader::Attribute;
25using Tegra::Shader::AttributeUse; 27using Tegra::Shader::AttributeUse;
26using Tegra::Shader::Header; 28using Tegra::Shader::Header;
@@ -34,13 +36,15 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
34using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage; 36using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage;
35using Operation = const OperationNode&; 37using Operation = const OperationNode&;
36 38
39enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat };
40
41struct TextureAoffi {};
42using TextureArgument = std::pair<Type, Node>;
43using TextureIR = std::variant<TextureAoffi, TextureArgument>;
44
37enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 }; 45enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
38constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 46constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
39 static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float)); 47 static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float));
40constexpr u32 MAX_GLOBALMEMORY_ELEMENTS =
41 static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize) / sizeof(float);
42
43enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat };
44 48
45class ShaderWriter { 49class ShaderWriter {
46public: 50public:
@@ -69,10 +73,10 @@ public:
69 shader_source += '\n'; 73 shader_source += '\n';
70 } 74 }
71 75
72 std::string GenerateTemporal() { 76 std::string GenerateTemporary() {
73 std::string temporal = "tmp"; 77 std::string temporary = "tmp";
74 temporal += std::to_string(temporal_index++); 78 temporary += std::to_string(temporary_index++);
75 return temporal; 79 return temporary;
76 } 80 }
77 81
78 std::string GetResult() { 82 std::string GetResult() {
@@ -87,11 +91,11 @@ private:
87 } 91 }
88 92
89 std::string shader_source; 93 std::string shader_source;
90 u32 temporal_index = 1; 94 u32 temporary_index = 1;
91}; 95};
92 96
93/// Generates code to use for a swizzle operation. 97/// Generates code to use for a swizzle operation.
94static std::string GetSwizzle(u32 elem) { 98std::string GetSwizzle(u32 elem) {
95 ASSERT(elem <= 3); 99 ASSERT(elem <= 3);
96 std::string swizzle = "."; 100 std::string swizzle = ".";
97 swizzle += "xyzw"[elem]; 101 swizzle += "xyzw"[elem];
@@ -99,7 +103,7 @@ static std::string GetSwizzle(u32 elem) {
99} 103}
100 104
101/// Translate topology 105/// Translate topology
102static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { 106std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
103 switch (topology) { 107 switch (topology) {
104 case Tegra::Shader::OutputTopology::PointList: 108 case Tegra::Shader::OutputTopology::PointList:
105 return "points"; 109 return "points";
@@ -114,7 +118,7 @@ static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
114} 118}
115 119
116/// Returns true if an object has to be treated as precise 120/// Returns true if an object has to be treated as precise
117static bool IsPrecise(Operation operand) { 121bool IsPrecise(Operation operand) {
118 const auto& meta = operand.GetMeta(); 122 const auto& meta = operand.GetMeta();
119 123
120 if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) { 124 if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) {
@@ -126,7 +130,7 @@ static bool IsPrecise(Operation operand) {
126 return false; 130 return false;
127} 131}
128 132
129static bool IsPrecise(Node node) { 133bool IsPrecise(Node node) {
130 if (const auto operation = std::get_if<OperationNode>(node)) { 134 if (const auto operation = std::get_if<OperationNode>(node)) {
131 return IsPrecise(*operation); 135 return IsPrecise(*operation);
132 } 136 }
@@ -202,8 +206,10 @@ public:
202 for (const auto& sampler : ir.GetSamplers()) { 206 for (const auto& sampler : ir.GetSamplers()) {
203 entries.samplers.emplace_back(sampler); 207 entries.samplers.emplace_back(sampler);
204 } 208 }
205 for (const auto& gmem : ir.GetGlobalMemoryBases()) { 209 for (const auto& gmem_pair : ir.GetGlobalMemory()) {
206 entries.global_memory_entries.emplace_back(gmem.cbuf_index, gmem.cbuf_offset); 210 const auto& [base, usage] = gmem_pair;
211 entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset,
212 usage.is_read, usage.is_written);
207 } 213 }
208 entries.clip_distances = ir.GetClipDistances(); 214 entries.clip_distances = ir.GetClipDistances();
209 entries.shader_length = ir.GetLength(); 215 entries.shader_length = ir.GetLength();
@@ -374,12 +380,22 @@ private:
374 } 380 }
375 381
376 void DeclareGlobalMemory() { 382 void DeclareGlobalMemory() {
377 for (const auto& entry : ir.GetGlobalMemoryBases()) { 383 for (const auto& gmem : ir.GetGlobalMemory()) {
384 const auto& [base, usage] = gmem;
385
386 // Since we don't know how the shader will use the shader, hint the driver to disable as
387 // much optimizations as possible
388 std::string qualifier = "coherent volatile";
389 if (usage.is_read && !usage.is_written)
390 qualifier += " readonly";
391 else if (usage.is_written && !usage.is_read)
392 qualifier += " writeonly";
393
378 const std::string binding = 394 const std::string binding =
379 fmt::format("GMEM_BINDING_{}_{}", entry.cbuf_index, entry.cbuf_offset); 395 fmt::format("GMEM_BINDING_{}_{}", base.cbuf_index, base.cbuf_offset);
380 code.AddLine("layout (std430, binding = " + binding + ") buffer " + 396 code.AddLine("layout (std430, binding = " + binding + ") " + qualifier + " buffer " +
381 GetGlobalMemoryBlock(entry) + " {"); 397 GetGlobalMemoryBlock(base) + " {");
382 code.AddLine(" float " + GetGlobalMemory(entry) + "[MAX_GLOBALMEMORY_ELEMENTS];"); 398 code.AddLine(" float " + GetGlobalMemory(base) + "[];");
383 code.AddLine("};"); 399 code.AddLine("};");
384 code.AddNewLine(); 400 code.AddNewLine();
385 } 401 }
@@ -426,9 +442,14 @@ private:
426 std::string Visit(Node node) { 442 std::string Visit(Node node) {
427 if (const auto operation = std::get_if<OperationNode>(node)) { 443 if (const auto operation = std::get_if<OperationNode>(node)) {
428 const auto operation_index = static_cast<std::size_t>(operation->GetCode()); 444 const auto operation_index = static_cast<std::size_t>(operation->GetCode());
445 if (operation_index >= operation_decompilers.size()) {
446 UNREACHABLE_MSG("Out of bounds operation: {}", operation_index);
447 return {};
448 }
429 const auto decompiler = operation_decompilers[operation_index]; 449 const auto decompiler = operation_decompilers[operation_index];
430 if (decompiler == nullptr) { 450 if (decompiler == nullptr) {
431 UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index); 451 UNREACHABLE_MSG("Undefined operation: {}", operation_index);
452 return {};
432 } 453 }
433 return (this->*decompiler)(*operation); 454 return (this->*decompiler)(*operation);
434 455
@@ -540,9 +561,8 @@ private:
540 561
541 } else if (std::holds_alternative<OperationNode>(*offset)) { 562 } else if (std::holds_alternative<OperationNode>(*offset)) {
542 // Indirect access 563 // Indirect access
543 const std::string final_offset = code.GenerateTemporal(); 564 const std::string final_offset = code.GenerateTemporary();
544 code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4) & " + 565 code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);");
545 std::to_string(MAX_CONSTBUFFER_ELEMENTS - 1) + ';');
546 return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), 566 return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
547 final_offset, final_offset); 567 final_offset, final_offset);
548 568
@@ -587,9 +607,9 @@ private:
587 // There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders 607 // There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders
588 const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; 608 const std::string precise = stage != ShaderStage::Fragment ? "precise " : "";
589 609
590 const std::string temporal = code.GenerateTemporal(); 610 const std::string temporary = code.GenerateTemporary();
591 code.AddLine(precise + "float " + temporal + " = " + value + ';'); 611 code.AddLine(precise + "float " + temporary + " = " + value + ';');
592 return temporal; 612 return temporary;
593 } 613 }
594 614
595 std::string VisitOperand(Operation operation, std::size_t operand_index) { 615 std::string VisitOperand(Operation operation, std::size_t operand_index) {
@@ -601,9 +621,9 @@ private:
601 return Visit(operand); 621 return Visit(operand);
602 } 622 }
603 623
604 const std::string temporal = code.GenerateTemporal(); 624 const std::string temporary = code.GenerateTemporary();
605 code.AddLine("float " + temporal + " = " + Visit(operand) + ';'); 625 code.AddLine("float " + temporary + " = " + Visit(operand) + ';');
606 return temporal; 626 return temporary;
607 } 627 }
608 628
609 std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { 629 std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) {
@@ -718,8 +738,8 @@ private:
718 result_type)); 738 result_type));
719 } 739 }
720 740
721 std::string GenerateTexture(Operation operation, const std::string& func, 741 std::string GenerateTexture(Operation operation, const std::string& function_suffix,
722 const std::vector<std::pair<Type, Node>>& extras) { 742 const std::vector<TextureIR>& extras) {
723 constexpr std::array<const char*, 4> coord_constructors = {"float", "vec2", "vec3", "vec4"}; 743 constexpr std::array<const char*, 4> coord_constructors = {"float", "vec2", "vec3", "vec4"};
724 744
725 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 745 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
@@ -729,11 +749,11 @@ private:
729 const bool has_array = meta->sampler.IsArray(); 749 const bool has_array = meta->sampler.IsArray();
730 const bool has_shadow = meta->sampler.IsShadow(); 750 const bool has_shadow = meta->sampler.IsShadow();
731 751
732 std::string expr = func; 752 std::string expr = "texture" + function_suffix;
733 expr += '('; 753 if (!meta->aoffi.empty()) {
734 expr += GetSampler(meta->sampler); 754 expr += "Offset";
735 expr += ", "; 755 }
736 756 expr += '(' + GetSampler(meta->sampler) + ", ";
737 expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1); 757 expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1);
738 expr += '('; 758 expr += '(';
739 for (std::size_t i = 0; i < count; ++i) { 759 for (std::size_t i = 0; i < count; ++i) {
@@ -751,36 +771,74 @@ private:
751 } 771 }
752 expr += ')'; 772 expr += ')';
753 773
754 for (const auto& extra_pair : extras) { 774 for (const auto& variant : extras) {
755 const auto [type, operand] = extra_pair; 775 if (const auto argument = std::get_if<TextureArgument>(&variant)) {
756 if (operand == nullptr) { 776 expr += GenerateTextureArgument(*argument);
757 continue; 777 } else if (std::get_if<TextureAoffi>(&variant)) {
778 expr += GenerateTextureAoffi(meta->aoffi);
779 } else {
780 UNREACHABLE();
758 } 781 }
759 expr += ", "; 782 }
760 783
761 switch (type) { 784 return expr + ')';
762 case Type::Int: 785 }
763 if (const auto immediate = std::get_if<ImmediateNode>(operand)) { 786
764 // Inline the string as an immediate integer in GLSL (some extra arguments are 787 std::string GenerateTextureArgument(TextureArgument argument) {
765 // required to be constant) 788 const auto [type, operand] = argument;
766 expr += std::to_string(static_cast<s32>(immediate->GetValue())); 789 if (operand == nullptr) {
767 } else { 790 return {};
768 expr += "ftoi(" + Visit(operand) + ')'; 791 }
769 } 792
770 break; 793 std::string expr = ", ";
771 case Type::Float: 794 switch (type) {
772 expr += Visit(operand); 795 case Type::Int:
773 break; 796 if (const auto immediate = std::get_if<ImmediateNode>(operand)) {
774 default: { 797 // Inline the string as an immediate integer in GLSL (some extra arguments are
775 const auto type_int = static_cast<u32>(type); 798 // required to be constant)
776 UNIMPLEMENTED_MSG("Unimplemented extra type={}", type_int); 799 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
777 expr += '0'; 800 } else {
778 break; 801 expr += "ftoi(" + Visit(operand) + ')';
802 }
803 break;
804 case Type::Float:
805 expr += Visit(operand);
806 break;
807 default: {
808 const auto type_int = static_cast<u32>(type);
809 UNIMPLEMENTED_MSG("Unimplemented extra type={}", type_int);
810 expr += '0';
811 break;
812 }
813 }
814 return expr;
815 }
816
817 std::string GenerateTextureAoffi(const std::vector<Node>& aoffi) {
818 if (aoffi.empty()) {
819 return {};
820 }
821 constexpr std::array<const char*, 3> coord_constructors = {"int", "ivec2", "ivec3"};
822 std::string expr = ", ";
823 expr += coord_constructors.at(aoffi.size() - 1);
824 expr += '(';
825
826 for (std::size_t index = 0; index < aoffi.size(); ++index) {
827 const auto operand{aoffi.at(index)};
828 if (const auto immediate = std::get_if<ImmediateNode>(operand)) {
829 // Inline the string as an immediate integer in GLSL (AOFFI arguments are required
830 // to be constant by the standard).
831 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
832 } else {
833 expr += "ftoi(" + Visit(operand) + ')';
779 } 834 }
835 if (index + 1 < aoffi.size()) {
836 expr += ", ";
780 } 837 }
781 } 838 }
839 expr += ')';
782 840
783 return expr + ')'; 841 return expr;
784 } 842 }
785 843
786 std::string Assign(Operation operation) { 844 std::string Assign(Operation operation) {
@@ -820,6 +878,12 @@ private:
820 } else if (const auto lmem = std::get_if<LmemNode>(dest)) { 878 } else if (const auto lmem = std::get_if<LmemNode>(dest)) {
821 target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; 879 target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]";
822 880
881 } else if (const auto gmem = std::get_if<GmemNode>(dest)) {
882 const std::string real = Visit(gmem->GetRealAddress());
883 const std::string base = Visit(gmem->GetBaseAddress());
884 const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4";
885 target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
886
823 } else { 887 } else {
824 UNREACHABLE_MSG("Assign called without a proper target"); 888 UNREACHABLE_MSG("Assign called without a proper target");
825 } 889 }
@@ -1159,7 +1223,8 @@ private:
1159 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1223 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
1160 ASSERT(meta); 1224 ASSERT(meta);
1161 1225
1162 std::string expr = GenerateTexture(operation, "texture", {{Type::Float, meta->bias}}); 1226 std::string expr = GenerateTexture(
1227 operation, "", {TextureAoffi{}, TextureArgument{Type::Float, meta->bias}});
1163 if (meta->sampler.IsShadow()) { 1228 if (meta->sampler.IsShadow()) {
1164 expr = "vec4(" + expr + ')'; 1229 expr = "vec4(" + expr + ')';
1165 } 1230 }
@@ -1170,7 +1235,8 @@ private:
1170 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1235 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
1171 ASSERT(meta); 1236 ASSERT(meta);
1172 1237
1173 std::string expr = GenerateTexture(operation, "textureLod", {{Type::Float, meta->lod}}); 1238 std::string expr = GenerateTexture(
1239 operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureAoffi{}});
1174 if (meta->sampler.IsShadow()) { 1240 if (meta->sampler.IsShadow()) {
1175 expr = "vec4(" + expr + ')'; 1241 expr = "vec4(" + expr + ')';
1176 } 1242 }
@@ -1182,7 +1248,8 @@ private:
1182 ASSERT(meta); 1248 ASSERT(meta);
1183 1249
1184 const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int; 1250 const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int;
1185 return GenerateTexture(operation, "textureGather", {{type, meta->component}}) + 1251 return GenerateTexture(operation, "Gather",
1252 {TextureArgument{type, meta->component}, TextureAoffi{}}) +
1186 GetSwizzle(meta->element); 1253 GetSwizzle(meta->element);
1187 } 1254 }
1188 1255
@@ -1196,11 +1263,12 @@ private:
1196 switch (meta->element) { 1263 switch (meta->element) {
1197 case 0: 1264 case 0:
1198 case 1: 1265 case 1:
1199 return "textureSize(" + sampler + ", " + lod + ')' + GetSwizzle(meta->element); 1266 return "itof(int(textureSize(" + sampler + ", " + lod + ')' +
1267 GetSwizzle(meta->element) + "))";
1200 case 2: 1268 case 2:
1201 return "0"; 1269 return "0";
1202 case 3: 1270 case 3:
1203 return "textureQueryLevels(" + sampler + ')'; 1271 return "itof(textureQueryLevels(" + sampler + "))";
1204 } 1272 }
1205 UNREACHABLE(); 1273 UNREACHABLE();
1206 return "0"; 1274 return "0";
@@ -1211,8 +1279,8 @@ private:
1211 ASSERT(meta); 1279 ASSERT(meta);
1212 1280
1213 if (meta->element < 2) { 1281 if (meta->element < 2) {
1214 return "itof(int((" + GenerateTexture(operation, "textureQueryLod", {}) + 1282 return "itof(int((" + GenerateTexture(operation, "QueryLod", {}) + " * vec2(256))" +
1215 " * vec2(256))" + GetSwizzle(meta->element) + "))"; 1283 GetSwizzle(meta->element) + "))";
1216 } 1284 }
1217 return "0"; 1285 return "0";
1218 } 1286 }
@@ -1565,11 +1633,11 @@ private:
1565 ShaderWriter code; 1633 ShaderWriter code;
1566}; 1634};
1567 1635
1636} // Anonymous namespace
1637
1568std::string GetCommonDeclarations() { 1638std::string GetCommonDeclarations() {
1569 const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS); 1639 const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS);
1570 const auto gmem = std::to_string(MAX_GLOBALMEMORY_ELEMENTS);
1571 return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" + 1640 return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" +
1572 "#define MAX_GLOBALMEMORY_ELEMENTS " + gmem + "\n" +
1573 "#define ftoi floatBitsToInt\n" 1641 "#define ftoi floatBitsToInt\n"
1574 "#define ftou floatBitsToUint\n" 1642 "#define ftou floatBitsToUint\n"
1575 "#define itof intBitsToFloat\n" 1643 "#define itof intBitsToFloat\n"
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index 72aca4938..55b3d4d7b 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <set>
9#include <string> 8#include <string>
10#include <utility> 9#include <utility>
11#include <vector> 10#include <vector>
@@ -40,8 +39,9 @@ private:
40 39
41class GlobalMemoryEntry { 40class GlobalMemoryEntry {
42public: 41public:
43 explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset) 42 explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset, bool is_read, bool is_written)
44 : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {} 43 : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset}, is_read{is_read}, is_written{
44 is_written} {}
45 45
46 u32 GetCbufIndex() const { 46 u32 GetCbufIndex() const {
47 return cbuf_index; 47 return cbuf_index;
@@ -51,9 +51,19 @@ public:
51 return cbuf_offset; 51 return cbuf_offset;
52 } 52 }
53 53
54 bool IsRead() const {
55 return is_read;
56 }
57
58 bool IsWritten() const {
59 return is_written;
60 }
61
54private: 62private:
55 u32 cbuf_index{}; 63 u32 cbuf_index{};
56 u32 cbuf_offset{}; 64 u32 cbuf_offset{};
65 bool is_read{};
66 bool is_written{};
57}; 67};
58 68
59struct ShaderEntries { 69struct ShaderEntries {
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 82fc4d44b..d5890a375 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -4,7 +4,6 @@
4 4
5#include <cstring> 5#include <cstring>
6#include <fmt/format.h> 6#include <fmt/format.h>
7#include <lz4.h>
8 7
9#include "common/assert.h" 8#include "common/assert.h"
10#include "common/common_paths.h" 9#include "common/common_paths.h"
@@ -12,6 +11,7 @@
12#include "common/file_util.h" 11#include "common/file_util.h"
13#include "common/logging/log.h" 12#include "common/logging/log.h"
14#include "common/scm_rev.h" 13#include "common/scm_rev.h"
14#include "common/zstd_compression.h"
15 15
16#include "core/core.h" 16#include "core/core.h"
17#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
@@ -49,39 +49,6 @@ ShaderCacheVersionHash GetShaderCacheVersionHash() {
49 return hash; 49 return hash;
50} 50}
51 51
52template <typename T>
53std::vector<u8> CompressData(const T* source, std::size_t source_size) {
54 if (source_size > LZ4_MAX_INPUT_SIZE) {
55 // Source size exceeds LZ4 maximum input size
56 return {};
57 }
58 const auto source_size_int = static_cast<int>(source_size);
59 const int max_compressed_size = LZ4_compressBound(source_size_int);
60 std::vector<u8> compressed(max_compressed_size);
61 const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source),
62 reinterpret_cast<char*>(compressed.data()),
63 source_size_int, max_compressed_size);
64 if (compressed_size <= 0) {
65 // Compression failed
66 return {};
67 }
68 compressed.resize(compressed_size);
69 return compressed;
70}
71
72std::vector<u8> DecompressData(const std::vector<u8>& compressed, std::size_t uncompressed_size) {
73 std::vector<u8> uncompressed(uncompressed_size);
74 const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()),
75 reinterpret_cast<char*>(uncompressed.data()),
76 static_cast<int>(compressed.size()),
77 static_cast<int>(uncompressed.size()));
78 if (static_cast<int>(uncompressed_size) != size_check) {
79 // Decompression failed
80 return {};
81 }
82 return uncompressed;
83}
84
85} // namespace 52} // namespace
86 53
87ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type, 54ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type,
@@ -292,7 +259,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
292 return {}; 259 return {};
293 } 260 }
294 261
295 dump.binary = DecompressData(compressed_binary, binary_length); 262 dump.binary = Common::Compression::DecompressDataZSTD(compressed_binary);
296 if (dump.binary.empty()) { 263 if (dump.binary.empty()) {
297 return {}; 264 return {};
298 } 265 }
@@ -321,7 +288,7 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
321 return {}; 288 return {};
322 } 289 }
323 290
324 const std::vector<u8> code = DecompressData(compressed_code, code_size); 291 const std::vector<u8> code = Common::Compression::DecompressDataZSTD(compressed_code);
325 if (code.empty()) { 292 if (code.empty()) {
326 return {}; 293 return {};
327 } 294 }
@@ -370,11 +337,16 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
370 for (u32 i = 0; i < global_memory_count; ++i) { 337 for (u32 i = 0; i < global_memory_count; ++i) {
371 u32 cbuf_index{}; 338 u32 cbuf_index{};
372 u32 cbuf_offset{}; 339 u32 cbuf_offset{};
340 u8 is_read{};
341 u8 is_written{};
373 if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) || 342 if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) ||
374 file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32)) { 343 file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32) ||
344 file.ReadBytes(&is_read, sizeof(u8)) != sizeof(u8) ||
345 file.ReadBytes(&is_written, sizeof(u8)) != sizeof(u8)) {
375 return {}; 346 return {};
376 } 347 }
377 entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset); 348 entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0,
349 is_written != 0);
378 } 350 }
379 351
380 for (auto& clip_distance : entry.entries.clip_distances) { 352 for (auto& clip_distance : entry.entries.clip_distances) {
@@ -430,7 +402,9 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 uniqu
430 return false; 402 return false;
431 for (const auto& gmem : entries.global_memory_entries) { 403 for (const auto& gmem : entries.global_memory_entries) {
432 if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 || 404 if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 ||
433 file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1) { 405 file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1 ||
406 file.WriteObject(static_cast<u8>(gmem.IsRead() ? 1 : 0)) != 1 ||
407 file.WriteObject(static_cast<u8>(gmem.IsWritten() ? 1 : 0)) != 1) {
434 return false; 408 return false;
435 } 409 }
436 } 410 }
@@ -507,7 +481,8 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str
507 if (!IsUsable()) 481 if (!IsUsable())
508 return; 482 return;
509 483
510 const std::vector<u8> compressed_code{CompressData(code.data(), code.size())}; 484 const std::vector<u8> compressed_code{Common::Compression::CompressDataZSTDDefault(
485 reinterpret_cast<const u8*>(code.data()), code.size())};
511 if (compressed_code.empty()) { 486 if (compressed_code.empty()) {
512 LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}", 487 LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}",
513 unique_identifier); 488 unique_identifier);
@@ -537,7 +512,9 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
537 std::vector<u8> binary(binary_length); 512 std::vector<u8> binary(binary_length);
538 glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); 513 glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data());
539 514
540 const std::vector<u8> compressed_binary = CompressData(binary.data(), binary.size()); 515 const std::vector<u8> compressed_binary =
516 Common::Compression::CompressDataZSTDDefault(binary.data(), binary.size());
517
541 if (compressed_binary.empty()) { 518 if (compressed_binary.empty()) {
542 LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}", 519 LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}",
543 usage.unique_identifier); 520 usage.unique_identifier);
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 7d96649af..8763d9c71 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.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 <fmt/format.h> 5#include <fmt/format.h>
6#include "common/assert.h"
7#include "video_core/engines/maxwell_3d.h" 6#include "video_core/engines/maxwell_3d.h"
8#include "video_core/renderer_opengl/gl_shader_decompiler.h" 7#include "video_core/renderer_opengl/gl_shader_decompiler.h"
9#include "video_core/renderer_opengl/gl_shader_gen.h" 8#include "video_core/renderer_opengl/gl_shader_gen.h"
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index fba8e681b..fad346b48 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -4,12 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <string>
9#include <vector> 7#include <vector>
10 8
11#include "common/common_types.h" 9#include "common/common_types.h"
12#include "video_core/engines/shader_bytecode.h"
13#include "video_core/renderer_opengl/gl_shader_decompiler.h" 10#include "video_core/renderer_opengl/gl_shader_decompiler.h"
14#include "video_core/shader/shader_ir.h" 11#include "video_core/shader/shader_ir.h"
15 12
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 6a30c28d2..05ab01dcb 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -2,23 +2,55 @@
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 "core/core.h" 5#include "common/common_types.h"
6#include "video_core/engines/maxwell_3d.h"
6#include "video_core/renderer_opengl/gl_shader_manager.h" 7#include "video_core/renderer_opengl/gl_shader_manager.h"
7 8
8namespace OpenGL::GLShader { 9namespace OpenGL::GLShader {
9 10
10void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) { 11using Tegra::Engines::Maxwell3D;
11 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 12
12 const auto& regs = gpu.regs; 13ProgramManager::ProgramManager() {
13 const auto& state = gpu.state; 14 pipeline.Create();
15}
16
17ProgramManager::~ProgramManager() = default;
18
19void ProgramManager::ApplyTo(OpenGLState& state) {
20 UpdatePipeline();
21 state.draw.shader_program = 0;
22 state.draw.program_pipeline = pipeline.handle;
23}
24
25void ProgramManager::UpdatePipeline() {
26 // Avoid updating the pipeline when values have no changed
27 if (old_state == current_state) {
28 return;
29 }
30
31 // Workaround for AMD bug
32 constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT |
33 GL_FRAGMENT_SHADER_BIT};
34 glUseProgramStages(pipeline.handle, all_used_stages, 0);
35
36 glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_state.vertex_shader);
37 glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, current_state.geometry_shader);
38 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current_state.fragment_shader);
39
40 old_state = current_state;
41}
42
43void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) {
44 const auto& regs = maxwell.regs;
45 const auto& state = maxwell.state;
14 46
15 // TODO(bunnei): Support more than one viewport 47 // TODO(bunnei): Support more than one viewport
16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; 48 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
17 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; 49 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
18 50
19 u32 func = static_cast<u32>(regs.alpha_test_func); 51 auto func{static_cast<u32>(regs.alpha_test_func)};
20 // Normalize the gl variants of opCompare to be the same as the normal variants 52 // Normalize the gl variants of opCompare to be the same as the normal variants
21 u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never); 53 const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never);
22 if (func >= op_gl_variant_base) { 54 if (func >= op_gl_variant_base) {
23 func = func - op_gl_variant_base + 1U; 55 func = func - op_gl_variant_base + 1U;
24 } 56 }
@@ -31,8 +63,9 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
31 63
32 // Assign in which stage the position has to be flipped 64 // Assign in which stage the position has to be flipped
33 // (the last stage before the fragment shader). 65 // (the last stage before the fragment shader).
34 if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) { 66 constexpr u32 geometry_index = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
35 flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); 67 if (maxwell.regs.shader_config[geometry_index].enable) {
68 flip_stage = geometry_index;
36 } else { 69 } else {
37 flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); 70 flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
38 } 71 }
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 4970aafed..cec18a832 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstddef>
8
7#include <glad/glad.h> 9#include <glad/glad.h>
8 10
9#include "video_core/renderer_opengl/gl_resource_manager.h" 11#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -12,14 +14,13 @@
12 14
13namespace OpenGL::GLShader { 15namespace OpenGL::GLShader {
14 16
15using Tegra::Engines::Maxwell3D;
16
17/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned 17/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned
18// NOTE: Always keep a vec4 at the end. The GL spec is not clear whether the alignment at 18/// @note Always keep a vec4 at the end. The GL spec is not clear whether the alignment at
19// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. 19/// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
20// Not following that rule will cause problems on some AMD drivers. 20/// Not following that rule will cause problems on some AMD drivers.
21struct MaxwellUniformData { 21struct MaxwellUniformData {
22 void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); 22 void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell, std::size_t shader_stage);
23
23 alignas(16) GLvec4 viewport_flip; 24 alignas(16) GLvec4 viewport_flip;
24 struct alignas(16) { 25 struct alignas(16) {
25 GLuint instance_id; 26 GLuint instance_id;
@@ -39,56 +40,48 @@ static_assert(sizeof(MaxwellUniformData) < 16384,
39 40
40class ProgramManager { 41class ProgramManager {
41public: 42public:
42 ProgramManager() { 43 explicit ProgramManager();
43 pipeline.Create(); 44 ~ProgramManager();
44 } 45
46 void ApplyTo(OpenGLState& state);
45 47
46 void UseProgrammableVertexShader(GLuint program) { 48 void UseProgrammableVertexShader(GLuint program) {
47 vs = program; 49 current_state.vertex_shader = program;
48 } 50 }
49 51
50 void UseProgrammableGeometryShader(GLuint program) { 52 void UseProgrammableGeometryShader(GLuint program) {
51 gs = program; 53 current_state.geometry_shader = program;
52 } 54 }
53 55
54 void UseProgrammableFragmentShader(GLuint program) { 56 void UseProgrammableFragmentShader(GLuint program) {
55 fs = program; 57 current_state.fragment_shader = program;
56 } 58 }
57 59
58 void UseTrivialGeometryShader() { 60 void UseTrivialGeometryShader() {
59 gs = 0; 61 current_state.geometry_shader = 0;
60 }
61
62 void ApplyTo(OpenGLState& state) {
63 UpdatePipeline();
64 state.draw.shader_program = 0;
65 state.draw.program_pipeline = pipeline.handle;
66 state.geometry_shaders.enabled = (gs != 0);
67 } 62 }
68 63
69private: 64private:
70 void UpdatePipeline() { 65 struct PipelineState {
71 // Avoid updating the pipeline when values have no changed 66 bool operator==(const PipelineState& rhs) const {
72 if (old_vs == vs && old_fs == fs && old_gs == gs) 67 return vertex_shader == rhs.vertex_shader && fragment_shader == rhs.fragment_shader &&
73 return; 68 geometry_shader == rhs.geometry_shader;
74 // Workaround for AMD bug 69 }
75 glUseProgramStages(pipeline.handle, 70
76 GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, 71 bool operator!=(const PipelineState& rhs) const {
77 0); 72 return !operator==(rhs);
78 73 }
79 glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vs); 74
80 glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, gs); 75 GLuint vertex_shader{};
81 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); 76 GLuint fragment_shader{};
82 77 GLuint geometry_shader{};
83 // Update the old values 78 };
84 old_vs = vs; 79
85 old_fs = fs; 80 void UpdatePipeline();
86 old_gs = gs;
87 }
88 81
89 OGLPipeline pipeline; 82 OGLPipeline pipeline;
90 GLuint vs{}, fs{}, gs{}; 83 PipelineState current_state;
91 GLuint old_vs{}, old_fs{}, old_gs{}; 84 PipelineState old_state;
92}; 85};
93 86
94} // namespace OpenGL::GLShader 87} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 9419326a3..52d569a1b 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -10,16 +10,62 @@
10 10
11namespace OpenGL { 11namespace OpenGL {
12 12
13OpenGLState OpenGLState::cur_state; 13using Maxwell = Tegra::Engines::Maxwell3D::Regs;
14 14
15OpenGLState OpenGLState::cur_state;
15bool OpenGLState::s_rgb_used; 16bool OpenGLState::s_rgb_used;
16 17
18namespace {
19
20template <typename T>
21bool UpdateValue(T& current_value, const T new_value) {
22 const bool changed = current_value != new_value;
23 current_value = new_value;
24 return changed;
25}
26
27template <typename T1, typename T2>
28bool UpdateTie(T1 current_value, const T2 new_value) {
29 const bool changed = current_value != new_value;
30 current_value = new_value;
31 return changed;
32}
33
34void Enable(GLenum cap, bool enable) {
35 if (enable) {
36 glEnable(cap);
37 } else {
38 glDisable(cap);
39 }
40}
41
42void Enable(GLenum cap, GLuint index, bool enable) {
43 if (enable) {
44 glEnablei(cap, index);
45 } else {
46 glDisablei(cap, index);
47 }
48}
49
50void Enable(GLenum cap, bool& current_value, bool new_value) {
51 if (UpdateValue(current_value, new_value))
52 Enable(cap, new_value);
53}
54
55void Enable(GLenum cap, GLuint index, bool& current_value, bool new_value) {
56 if (UpdateValue(current_value, new_value))
57 Enable(cap, index, new_value);
58}
59
60} // namespace
61
17OpenGLState::OpenGLState() { 62OpenGLState::OpenGLState() {
18 // These all match default OpenGL values 63 // These all match default OpenGL values
19 geometry_shaders.enabled = false;
20 framebuffer_srgb.enabled = false; 64 framebuffer_srgb.enabled = false;
65
21 multisample_control.alpha_to_coverage = false; 66 multisample_control.alpha_to_coverage = false;
22 multisample_control.alpha_to_one = false; 67 multisample_control.alpha_to_one = false;
68
23 cull.enabled = false; 69 cull.enabled = false;
24 cull.mode = GL_BACK; 70 cull.mode = GL_BACK;
25 cull.front_face = GL_CCW; 71 cull.front_face = GL_CCW;
@@ -30,14 +76,15 @@ OpenGLState::OpenGLState() {
30 76
31 primitive_restart.enabled = false; 77 primitive_restart.enabled = false;
32 primitive_restart.index = 0; 78 primitive_restart.index = 0;
79
33 for (auto& item : color_mask) { 80 for (auto& item : color_mask) {
34 item.red_enabled = GL_TRUE; 81 item.red_enabled = GL_TRUE;
35 item.green_enabled = GL_TRUE; 82 item.green_enabled = GL_TRUE;
36 item.blue_enabled = GL_TRUE; 83 item.blue_enabled = GL_TRUE;
37 item.alpha_enabled = GL_TRUE; 84 item.alpha_enabled = GL_TRUE;
38 } 85 }
39 stencil.test_enabled = false; 86
40 auto reset_stencil = [](auto& config) { 87 const auto ResetStencil = [](auto& config) {
41 config.test_func = GL_ALWAYS; 88 config.test_func = GL_ALWAYS;
42 config.test_ref = 0; 89 config.test_ref = 0;
43 config.test_mask = 0xFFFFFFFF; 90 config.test_mask = 0xFFFFFFFF;
@@ -46,8 +93,10 @@ OpenGLState::OpenGLState() {
46 config.action_depth_pass = GL_KEEP; 93 config.action_depth_pass = GL_KEEP;
47 config.action_stencil_fail = GL_KEEP; 94 config.action_stencil_fail = GL_KEEP;
48 }; 95 };
49 reset_stencil(stencil.front); 96 stencil.test_enabled = false;
50 reset_stencil(stencil.back); 97 ResetStencil(stencil.front);
98 ResetStencil(stencil.back);
99
51 for (auto& item : viewports) { 100 for (auto& item : viewports) {
52 item.x = 0; 101 item.x = 0;
53 item.y = 0; 102 item.y = 0;
@@ -61,6 +110,7 @@ OpenGLState::OpenGLState() {
61 item.scissor.width = 0; 110 item.scissor.width = 0;
62 item.scissor.height = 0; 111 item.scissor.height = 0;
63 } 112 }
113
64 for (auto& item : blend) { 114 for (auto& item : blend) {
65 item.enabled = true; 115 item.enabled = true;
66 item.rgb_equation = GL_FUNC_ADD; 116 item.rgb_equation = GL_FUNC_ADD;
@@ -70,11 +120,14 @@ OpenGLState::OpenGLState() {
70 item.src_a_func = GL_ONE; 120 item.src_a_func = GL_ONE;
71 item.dst_a_func = GL_ZERO; 121 item.dst_a_func = GL_ZERO;
72 } 122 }
123
73 independant_blend.enabled = false; 124 independant_blend.enabled = false;
125
74 blend_color.red = 0.0f; 126 blend_color.red = 0.0f;
75 blend_color.green = 0.0f; 127 blend_color.green = 0.0f;
76 blend_color.blue = 0.0f; 128 blend_color.blue = 0.0f;
77 blend_color.alpha = 0.0f; 129 blend_color.alpha = 0.0f;
130
78 logic_op.enabled = false; 131 logic_op.enabled = false;
79 logic_op.operation = GL_COPY; 132 logic_op.operation = GL_COPY;
80 133
@@ -91,9 +144,12 @@ OpenGLState::OpenGLState() {
91 clip_distance = {}; 144 clip_distance = {};
92 145
93 point.size = 1; 146 point.size = 1;
147
94 fragment_color_clamp.enabled = false; 148 fragment_color_clamp.enabled = false;
149
95 depth_clamp.far_plane = false; 150 depth_clamp.far_plane = false;
96 depth_clamp.near_plane = false; 151 depth_clamp.near_plane = false;
152
97 polygon_offset.fill_enable = false; 153 polygon_offset.fill_enable = false;
98 polygon_offset.line_enable = false; 154 polygon_offset.line_enable = false;
99 polygon_offset.point_enable = false; 155 polygon_offset.point_enable = false;
@@ -103,260 +159,255 @@ OpenGLState::OpenGLState() {
103} 159}
104 160
105void OpenGLState::ApplyDefaultState() { 161void OpenGLState::ApplyDefaultState() {
162 glEnable(GL_BLEND);
106 glDisable(GL_FRAMEBUFFER_SRGB); 163 glDisable(GL_FRAMEBUFFER_SRGB);
107 glDisable(GL_CULL_FACE); 164 glDisable(GL_CULL_FACE);
108 glDisable(GL_DEPTH_TEST); 165 glDisable(GL_DEPTH_TEST);
109 glDisable(GL_PRIMITIVE_RESTART); 166 glDisable(GL_PRIMITIVE_RESTART);
110 glDisable(GL_STENCIL_TEST); 167 glDisable(GL_STENCIL_TEST);
111 glEnable(GL_BLEND);
112 glDisable(GL_COLOR_LOGIC_OP); 168 glDisable(GL_COLOR_LOGIC_OP);
113 glDisable(GL_SCISSOR_TEST); 169 glDisable(GL_SCISSOR_TEST);
114} 170}
115 171
172void OpenGLState::ApplyFramebufferState() const {
173 if (UpdateValue(cur_state.draw.read_framebuffer, draw.read_framebuffer)) {
174 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
175 }
176 if (UpdateValue(cur_state.draw.draw_framebuffer, draw.draw_framebuffer)) {
177 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
178 }
179}
180
181void OpenGLState::ApplyVertexArrayState() const {
182 if (UpdateValue(cur_state.draw.vertex_array, draw.vertex_array)) {
183 glBindVertexArray(draw.vertex_array);
184 }
185}
186
187void OpenGLState::ApplyShaderProgram() const {
188 if (UpdateValue(cur_state.draw.shader_program, draw.shader_program)) {
189 glUseProgram(draw.shader_program);
190 }
191}
192
193void OpenGLState::ApplyProgramPipeline() const {
194 if (UpdateValue(cur_state.draw.program_pipeline, draw.program_pipeline)) {
195 glBindProgramPipeline(draw.program_pipeline);
196 }
197}
198
199void OpenGLState::ApplyClipDistances() const {
200 for (std::size_t i = 0; i < clip_distance.size(); ++i) {
201 Enable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i), cur_state.clip_distance[i],
202 clip_distance[i]);
203 }
204}
205
206void OpenGLState::ApplyPointSize() const {
207 if (UpdateValue(cur_state.point.size, point.size)) {
208 glPointSize(point.size);
209 }
210}
211
212void OpenGLState::ApplyFragmentColorClamp() const {
213 if (UpdateValue(cur_state.fragment_color_clamp.enabled, fragment_color_clamp.enabled)) {
214 glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
215 fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
216 }
217}
218
219void OpenGLState::ApplyMultisample() const {
220 Enable(GL_SAMPLE_ALPHA_TO_COVERAGE, cur_state.multisample_control.alpha_to_coverage,
221 multisample_control.alpha_to_coverage);
222 Enable(GL_SAMPLE_ALPHA_TO_ONE, cur_state.multisample_control.alpha_to_one,
223 multisample_control.alpha_to_one);
224}
225
226void OpenGLState::ApplyDepthClamp() const {
227 if (depth_clamp.far_plane == cur_state.depth_clamp.far_plane &&
228 depth_clamp.near_plane == cur_state.depth_clamp.near_plane) {
229 return;
230 }
231 cur_state.depth_clamp = depth_clamp;
232
233 UNIMPLEMENTED_IF_MSG(depth_clamp.far_plane != depth_clamp.near_plane,
234 "Unimplemented Depth Clamp Separation!");
235
236 Enable(GL_DEPTH_CLAMP, depth_clamp.far_plane || depth_clamp.near_plane);
237}
238
116void OpenGLState::ApplySRgb() const { 239void OpenGLState::ApplySRgb() const {
117 if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) { 240 if (cur_state.framebuffer_srgb.enabled == framebuffer_srgb.enabled)
118 if (framebuffer_srgb.enabled) { 241 return;
119 // Track if sRGB is used 242 cur_state.framebuffer_srgb.enabled = framebuffer_srgb.enabled;
120 s_rgb_used = true; 243 if (framebuffer_srgb.enabled) {
121 glEnable(GL_FRAMEBUFFER_SRGB); 244 // Track if sRGB is used
122 } else { 245 s_rgb_used = true;
123 glDisable(GL_FRAMEBUFFER_SRGB); 246 glEnable(GL_FRAMEBUFFER_SRGB);
124 } 247 } else {
248 glDisable(GL_FRAMEBUFFER_SRGB);
125 } 249 }
126} 250}
127 251
128void OpenGLState::ApplyCulling() const { 252void OpenGLState::ApplyCulling() const {
129 if (cull.enabled != cur_state.cull.enabled) { 253 Enable(GL_CULL_FACE, cur_state.cull.enabled, cull.enabled);
130 if (cull.enabled) {
131 glEnable(GL_CULL_FACE);
132 } else {
133 glDisable(GL_CULL_FACE);
134 }
135 }
136 254
137 if (cull.mode != cur_state.cull.mode) { 255 if (UpdateValue(cur_state.cull.mode, cull.mode)) {
138 glCullFace(cull.mode); 256 glCullFace(cull.mode);
139 } 257 }
140 258
141 if (cull.front_face != cur_state.cull.front_face) { 259 if (UpdateValue(cur_state.cull.front_face, cull.front_face)) {
142 glFrontFace(cull.front_face); 260 glFrontFace(cull.front_face);
143 } 261 }
144} 262}
145 263
146void OpenGLState::ApplyColorMask() const { 264void OpenGLState::ApplyColorMask() const {
147 if (independant_blend.enabled) { 265 for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
148 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 266 const auto& updated = color_mask[i];
149 const auto& updated = color_mask[i]; 267 auto& current = cur_state.color_mask[i];
150 const auto& current = cur_state.color_mask[i];
151 if (updated.red_enabled != current.red_enabled ||
152 updated.green_enabled != current.green_enabled ||
153 updated.blue_enabled != current.blue_enabled ||
154 updated.alpha_enabled != current.alpha_enabled) {
155 glColorMaski(static_cast<GLuint>(i), updated.red_enabled, updated.green_enabled,
156 updated.blue_enabled, updated.alpha_enabled);
157 }
158 }
159 } else {
160 const auto& updated = color_mask[0];
161 const auto& current = cur_state.color_mask[0];
162 if (updated.red_enabled != current.red_enabled || 268 if (updated.red_enabled != current.red_enabled ||
163 updated.green_enabled != current.green_enabled || 269 updated.green_enabled != current.green_enabled ||
164 updated.blue_enabled != current.blue_enabled || 270 updated.blue_enabled != current.blue_enabled ||
165 updated.alpha_enabled != current.alpha_enabled) { 271 updated.alpha_enabled != current.alpha_enabled) {
166 glColorMask(updated.red_enabled, updated.green_enabled, updated.blue_enabled, 272 current = updated;
167 updated.alpha_enabled); 273 glColorMaski(static_cast<GLuint>(i), updated.red_enabled, updated.green_enabled,
274 updated.blue_enabled, updated.alpha_enabled);
168 } 275 }
169 } 276 }
170} 277}
171 278
172void OpenGLState::ApplyDepth() const { 279void OpenGLState::ApplyDepth() const {
173 if (depth.test_enabled != cur_state.depth.test_enabled) { 280 Enable(GL_DEPTH_TEST, cur_state.depth.test_enabled, depth.test_enabled);
174 if (depth.test_enabled) {
175 glEnable(GL_DEPTH_TEST);
176 } else {
177 glDisable(GL_DEPTH_TEST);
178 }
179 }
180 281
181 if (depth.test_func != cur_state.depth.test_func) { 282 if (cur_state.depth.test_func != depth.test_func) {
283 cur_state.depth.test_func = depth.test_func;
182 glDepthFunc(depth.test_func); 284 glDepthFunc(depth.test_func);
183 } 285 }
184 286
185 if (depth.write_mask != cur_state.depth.write_mask) { 287 if (cur_state.depth.write_mask != depth.write_mask) {
288 cur_state.depth.write_mask = depth.write_mask;
186 glDepthMask(depth.write_mask); 289 glDepthMask(depth.write_mask);
187 } 290 }
188} 291}
189 292
190void OpenGLState::ApplyPrimitiveRestart() const { 293void OpenGLState::ApplyPrimitiveRestart() const {
191 if (primitive_restart.enabled != cur_state.primitive_restart.enabled) { 294 Enable(GL_PRIMITIVE_RESTART, cur_state.primitive_restart.enabled, primitive_restart.enabled);
192 if (primitive_restart.enabled) {
193 glEnable(GL_PRIMITIVE_RESTART);
194 } else {
195 glDisable(GL_PRIMITIVE_RESTART);
196 }
197 }
198 295
199 if (primitive_restart.index != cur_state.primitive_restart.index) { 296 if (cur_state.primitive_restart.index != primitive_restart.index) {
297 cur_state.primitive_restart.index = primitive_restart.index;
200 glPrimitiveRestartIndex(primitive_restart.index); 298 glPrimitiveRestartIndex(primitive_restart.index);
201 } 299 }
202} 300}
203 301
204void OpenGLState::ApplyStencilTest() const { 302void OpenGLState::ApplyStencilTest() const {
205 if (stencil.test_enabled != cur_state.stencil.test_enabled) { 303 Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled);
206 if (stencil.test_enabled) { 304
207 glEnable(GL_STENCIL_TEST); 305 const auto ConfigStencil = [](GLenum face, const auto& config, auto& current) {
208 } else { 306 if (current.test_func != config.test_func || current.test_ref != config.test_ref ||
209 glDisable(GL_STENCIL_TEST); 307 current.test_mask != config.test_mask) {
210 } 308 current.test_func = config.test_func;
211 } 309 current.test_ref = config.test_ref;
212 310 current.test_mask = config.test_mask;
213 const auto ConfigStencil = [](GLenum face, const auto& config, const auto& prev_config) {
214 if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref ||
215 config.test_mask != prev_config.test_mask) {
216 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); 311 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
217 } 312 }
218 if (config.action_depth_fail != prev_config.action_depth_fail || 313 if (current.action_depth_fail != config.action_depth_fail ||
219 config.action_depth_pass != prev_config.action_depth_pass || 314 current.action_depth_pass != config.action_depth_pass ||
220 config.action_stencil_fail != prev_config.action_stencil_fail) { 315 current.action_stencil_fail != config.action_stencil_fail) {
316 current.action_depth_fail = config.action_depth_fail;
317 current.action_depth_pass = config.action_depth_pass;
318 current.action_stencil_fail = config.action_stencil_fail;
221 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, 319 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
222 config.action_depth_pass); 320 config.action_depth_pass);
223 } 321 }
224 if (config.write_mask != prev_config.write_mask) { 322 if (current.write_mask != config.write_mask) {
323 current.write_mask = config.write_mask;
225 glStencilMaskSeparate(face, config.write_mask); 324 glStencilMaskSeparate(face, config.write_mask);
226 } 325 }
227 }; 326 };
228 ConfigStencil(GL_FRONT, stencil.front, cur_state.stencil.front); 327 ConfigStencil(GL_FRONT, stencil.front, cur_state.stencil.front);
229 ConfigStencil(GL_BACK, stencil.back, cur_state.stencil.back); 328 ConfigStencil(GL_BACK, stencil.back, cur_state.stencil.back);
230} 329}
231// Viewport does not affects glClearBuffer so emulate viewport using scissor test
232void OpenGLState::EmulateViewportWithScissor() {
233 auto& current = viewports[0];
234 if (current.scissor.enabled) {
235 const GLint left = std::max(current.x, current.scissor.x);
236 const GLint right =
237 std::max(current.x + current.width, current.scissor.x + current.scissor.width);
238 const GLint bottom = std::max(current.y, current.scissor.y);
239 const GLint top =
240 std::max(current.y + current.height, current.scissor.y + current.scissor.height);
241 current.scissor.x = std::max(left, 0);
242 current.scissor.y = std::max(bottom, 0);
243 current.scissor.width = std::max(right - left, 0);
244 current.scissor.height = std::max(top - bottom, 0);
245 } else {
246 current.scissor.enabled = true;
247 current.scissor.x = current.x;
248 current.scissor.y = current.y;
249 current.scissor.width = current.width;
250 current.scissor.height = current.height;
251 }
252}
253 330
254void OpenGLState::ApplyViewport() const { 331void OpenGLState::ApplyViewport() const {
255 if (geometry_shaders.enabled) { 332 for (GLuint i = 0; i < static_cast<GLuint>(Maxwell::NumViewports); ++i) {
256 for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports); 333 const auto& updated = viewports[i];
257 i++) { 334 auto& current = cur_state.viewports[i];
258 const auto& current = cur_state.viewports[i]; 335
259 const auto& updated = viewports[i]; 336 if (current.x != updated.x || current.y != updated.y || current.width != updated.width ||
260 if (updated.x != current.x || updated.y != current.y || 337 current.height != updated.height) {
261 updated.width != current.width || updated.height != current.height) { 338 current.x = updated.x;
262 glViewportIndexedf( 339 current.y = updated.y;
263 i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y), 340 current.width = updated.width;
264 static_cast<GLfloat>(updated.width), static_cast<GLfloat>(updated.height)); 341 current.height = updated.height;
265 } 342 glViewportIndexedf(i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y),
266 if (updated.depth_range_near != current.depth_range_near || 343 static_cast<GLfloat>(updated.width),
267 updated.depth_range_far != current.depth_range_far) { 344 static_cast<GLfloat>(updated.height));
268 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
269 }
270
271 if (updated.scissor.enabled != current.scissor.enabled) {
272 if (updated.scissor.enabled) {
273 glEnablei(GL_SCISSOR_TEST, i);
274 } else {
275 glDisablei(GL_SCISSOR_TEST, i);
276 }
277 }
278
279 if (updated.scissor.x != current.scissor.x || updated.scissor.y != current.scissor.y ||
280 updated.scissor.width != current.scissor.width ||
281 updated.scissor.height != current.scissor.height) {
282 glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
283 updated.scissor.height);
284 }
285 }
286 } else {
287 const auto& current = cur_state.viewports[0];
288 const auto& updated = viewports[0];
289 if (updated.x != current.x || updated.y != current.y || updated.width != current.width ||
290 updated.height != current.height) {
291 glViewport(updated.x, updated.y, updated.width, updated.height);
292 }
293
294 if (updated.depth_range_near != current.depth_range_near ||
295 updated.depth_range_far != current.depth_range_far) {
296 glDepthRange(updated.depth_range_near, updated.depth_range_far);
297 } 345 }
298 346 if (current.depth_range_near != updated.depth_range_near ||
299 if (updated.scissor.enabled != current.scissor.enabled) { 347 current.depth_range_far != updated.depth_range_far) {
300 if (updated.scissor.enabled) { 348 current.depth_range_near = updated.depth_range_near;
301 glEnable(GL_SCISSOR_TEST); 349 current.depth_range_far = updated.depth_range_far;
302 } else { 350 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
303 glDisable(GL_SCISSOR_TEST);
304 }
305 } 351 }
306 352
307 if (updated.scissor.x != current.scissor.x || updated.scissor.y != current.scissor.y || 353 Enable(GL_SCISSOR_TEST, i, current.scissor.enabled, updated.scissor.enabled);
308 updated.scissor.width != current.scissor.width || 354
309 updated.scissor.height != current.scissor.height) { 355 if (current.scissor.x != updated.scissor.x || current.scissor.y != updated.scissor.y ||
310 glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width, 356 current.scissor.width != updated.scissor.width ||
311 updated.scissor.height); 357 current.scissor.height != updated.scissor.height) {
358 current.scissor.x = updated.scissor.x;
359 current.scissor.y = updated.scissor.y;
360 current.scissor.width = updated.scissor.width;
361 current.scissor.height = updated.scissor.height;
362 glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
363 updated.scissor.height);
312 } 364 }
313 } 365 }
314} 366}
315 367
316void OpenGLState::ApplyGlobalBlending() const { 368void OpenGLState::ApplyGlobalBlending() const {
317 const Blend& current = cur_state.blend[0];
318 const Blend& updated = blend[0]; 369 const Blend& updated = blend[0];
319 if (updated.enabled != current.enabled) { 370 Blend& current = cur_state.blend[0];
320 if (updated.enabled) { 371
321 glEnable(GL_BLEND); 372 Enable(GL_BLEND, current.enabled, updated.enabled);
322 } else { 373
323 glDisable(GL_BLEND); 374 if (current.src_rgb_func != updated.src_rgb_func ||
324 } 375 current.dst_rgb_func != updated.dst_rgb_func || current.src_a_func != updated.src_a_func ||
325 } 376 current.dst_a_func != updated.dst_a_func) {
326 if (!updated.enabled) { 377 current.src_rgb_func = updated.src_rgb_func;
327 return; 378 current.dst_rgb_func = updated.dst_rgb_func;
328 } 379 current.src_a_func = updated.src_a_func;
329 if (updated.src_rgb_func != current.src_rgb_func || 380 current.dst_a_func = updated.dst_a_func;
330 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
331 updated.dst_a_func != current.dst_a_func) {
332 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, 381 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
333 updated.dst_a_func); 382 updated.dst_a_func);
334 } 383 }
335 384
336 if (updated.rgb_equation != current.rgb_equation || updated.a_equation != current.a_equation) { 385 if (current.rgb_equation != updated.rgb_equation || current.a_equation != updated.a_equation) {
386 current.rgb_equation = updated.rgb_equation;
387 current.a_equation = updated.a_equation;
337 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation); 388 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
338 } 389 }
339} 390}
340 391
341void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const { 392void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
342 const Blend& updated = blend[target]; 393 const Blend& updated = blend[target];
343 const Blend& current = cur_state.blend[target]; 394 Blend& current = cur_state.blend[target];
344 if (updated.enabled != current.enabled || force) { 395
345 if (updated.enabled) { 396 if (current.enabled != updated.enabled || force) {
346 glEnablei(GL_BLEND, static_cast<GLuint>(target)); 397 current.enabled = updated.enabled;
347 } else { 398 Enable(GL_BLEND, static_cast<GLuint>(target), updated.enabled);
348 glDisablei(GL_BLEND, static_cast<GLuint>(target));
349 }
350 } 399 }
351 400
352 if (updated.src_rgb_func != current.src_rgb_func || 401 if (UpdateTie(std::tie(current.src_rgb_func, current.dst_rgb_func, current.src_a_func,
353 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func || 402 current.dst_a_func),
354 updated.dst_a_func != current.dst_a_func) { 403 std::tie(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
404 updated.dst_a_func))) {
355 glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func, 405 glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func,
356 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); 406 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
357 } 407 }
358 408
359 if (updated.rgb_equation != current.rgb_equation || updated.a_equation != current.a_equation) { 409 if (UpdateTie(std::tie(current.rgb_equation, current.a_equation),
410 std::tie(updated.rgb_equation, updated.a_equation))) {
360 glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation, 411 glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation,
361 updated.a_equation); 412 updated.a_equation);
362 } 413 }
@@ -364,77 +415,48 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
364 415
365void OpenGLState::ApplyBlending() const { 416void OpenGLState::ApplyBlending() const {
366 if (independant_blend.enabled) { 417 if (independant_blend.enabled) {
367 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 418 const bool force = independant_blend.enabled != cur_state.independant_blend.enabled;
368 ApplyTargetBlending(i, 419 for (std::size_t target = 0; target < Maxwell::NumRenderTargets; ++target) {
369 independant_blend.enabled != cur_state.independant_blend.enabled); 420 ApplyTargetBlending(target, force);
370 } 421 }
371 } else { 422 } else {
372 ApplyGlobalBlending(); 423 ApplyGlobalBlending();
373 } 424 }
374 if (blend_color.red != cur_state.blend_color.red || 425 cur_state.independant_blend.enabled = independant_blend.enabled;
375 blend_color.green != cur_state.blend_color.green || 426
376 blend_color.blue != cur_state.blend_color.blue || 427 if (UpdateTie(
377 blend_color.alpha != cur_state.blend_color.alpha) { 428 std::tie(cur_state.blend_color.red, cur_state.blend_color.green,
429 cur_state.blend_color.blue, cur_state.blend_color.alpha),
430 std::tie(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha))) {
378 glBlendColor(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha); 431 glBlendColor(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha);
379 } 432 }
380} 433}
381 434
382void OpenGLState::ApplyLogicOp() const { 435void OpenGLState::ApplyLogicOp() const {
383 if (logic_op.enabled != cur_state.logic_op.enabled) { 436 Enable(GL_COLOR_LOGIC_OP, cur_state.logic_op.enabled, logic_op.enabled);
384 if (logic_op.enabled) {
385 glEnable(GL_COLOR_LOGIC_OP);
386 } else {
387 glDisable(GL_COLOR_LOGIC_OP);
388 }
389 }
390 437
391 if (logic_op.operation != cur_state.logic_op.operation) { 438 if (UpdateValue(cur_state.logic_op.operation, logic_op.operation)) {
392 glLogicOp(logic_op.operation); 439 glLogicOp(logic_op.operation);
393 } 440 }
394} 441}
395 442
396void OpenGLState::ApplyPolygonOffset() const { 443void OpenGLState::ApplyPolygonOffset() const {
397 const bool fill_enable_changed = 444 Enable(GL_POLYGON_OFFSET_FILL, cur_state.polygon_offset.fill_enable,
398 polygon_offset.fill_enable != cur_state.polygon_offset.fill_enable; 445 polygon_offset.fill_enable);
399 const bool line_enable_changed = 446 Enable(GL_POLYGON_OFFSET_LINE, cur_state.polygon_offset.line_enable,
400 polygon_offset.line_enable != cur_state.polygon_offset.line_enable; 447 polygon_offset.line_enable);
401 const bool point_enable_changed = 448 Enable(GL_POLYGON_OFFSET_POINT, cur_state.polygon_offset.point_enable,
402 polygon_offset.point_enable != cur_state.polygon_offset.point_enable; 449 polygon_offset.point_enable);
403 const bool factor_changed = polygon_offset.factor != cur_state.polygon_offset.factor; 450
404 const bool units_changed = polygon_offset.units != cur_state.polygon_offset.units; 451 if (UpdateTie(std::tie(cur_state.polygon_offset.factor, cur_state.polygon_offset.units,
405 const bool clamp_changed = polygon_offset.clamp != cur_state.polygon_offset.clamp; 452 cur_state.polygon_offset.clamp),
406 453 std::tie(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp))) {
407 if (fill_enable_changed) {
408 if (polygon_offset.fill_enable) {
409 glEnable(GL_POLYGON_OFFSET_FILL);
410 } else {
411 glDisable(GL_POLYGON_OFFSET_FILL);
412 }
413 }
414
415 if (line_enable_changed) {
416 if (polygon_offset.line_enable) {
417 glEnable(GL_POLYGON_OFFSET_LINE);
418 } else {
419 glDisable(GL_POLYGON_OFFSET_LINE);
420 }
421 }
422
423 if (point_enable_changed) {
424 if (polygon_offset.point_enable) {
425 glEnable(GL_POLYGON_OFFSET_POINT);
426 } else {
427 glDisable(GL_POLYGON_OFFSET_POINT);
428 }
429 }
430
431 if (factor_changed || units_changed || clamp_changed) {
432 if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) { 454 if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) {
433 glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp); 455 glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp);
434 } else { 456 } else {
435 glPolygonOffset(polygon_offset.factor, polygon_offset.units);
436 UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0, 457 UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0,
437 "Unimplemented Depth polygon offset clamp."); 458 "Unimplemented Depth polygon offset clamp.");
459 glPolygonOffset(polygon_offset.factor, polygon_offset.units);
438 } 460 }
439 } 461 }
440} 462}
@@ -443,22 +465,21 @@ void OpenGLState::ApplyTextures() const {
443 bool has_delta{}; 465 bool has_delta{};
444 std::size_t first{}; 466 std::size_t first{};
445 std::size_t last{}; 467 std::size_t last{};
446 std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> textures; 468 std::array<GLuint, Maxwell::NumTextureSamplers> textures;
447 469
448 for (std::size_t i = 0; i < std::size(texture_units); ++i) { 470 for (std::size_t i = 0; i < std::size(texture_units); ++i) {
449 const auto& texture_unit = texture_units[i]; 471 const auto& texture_unit = texture_units[i];
450 const auto& cur_state_texture_unit = cur_state.texture_units[i]; 472 auto& cur_state_texture_unit = cur_state.texture_units[i];
451 textures[i] = texture_unit.texture; 473 textures[i] = texture_unit.texture;
452 474 if (cur_state_texture_unit.texture == textures[i])
453 if (textures[i] != cur_state_texture_unit.texture) { 475 continue;
454 if (!has_delta) { 476 cur_state_texture_unit.texture = textures[i];
455 first = i; 477 if (!has_delta) {
456 has_delta = true; 478 first = i;
457 } 479 has_delta = true;
458 last = i;
459 } 480 }
481 last = i;
460 } 482 }
461
462 if (has_delta) { 483 if (has_delta) {
463 glBindTextures(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), 484 glBindTextures(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
464 textures.data() + first); 485 textures.data() + first);
@@ -469,16 +490,18 @@ void OpenGLState::ApplySamplers() const {
469 bool has_delta{}; 490 bool has_delta{};
470 std::size_t first{}; 491 std::size_t first{};
471 std::size_t last{}; 492 std::size_t last{};
472 std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; 493 std::array<GLuint, Maxwell::NumTextureSamplers> samplers;
494
473 for (std::size_t i = 0; i < std::size(samplers); ++i) { 495 for (std::size_t i = 0; i < std::size(samplers); ++i) {
496 if (cur_state.texture_units[i].sampler == texture_units[i].sampler)
497 continue;
498 cur_state.texture_units[i].sampler = texture_units[i].sampler;
474 samplers[i] = texture_units[i].sampler; 499 samplers[i] = texture_units[i].sampler;
475 if (samplers[i] != cur_state.texture_units[i].sampler) { 500 if (!has_delta) {
476 if (!has_delta) { 501 first = i;
477 first = i; 502 has_delta = true;
478 has_delta = true;
479 }
480 last = i;
481 } 503 }
504 last = i;
482 } 505 }
483 if (has_delta) { 506 if (has_delta) {
484 glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), 507 glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
@@ -486,81 +509,15 @@ void OpenGLState::ApplySamplers() const {
486 } 509 }
487} 510}
488 511
489void OpenGLState::ApplyFramebufferState() const {
490 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
491 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
492 }
493 if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) {
494 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
495 }
496}
497
498void OpenGLState::ApplyVertexArrayState() const {
499 if (draw.vertex_array != cur_state.draw.vertex_array) {
500 glBindVertexArray(draw.vertex_array);
501 }
502}
503
504void OpenGLState::ApplyDepthClamp() const {
505 if (depth_clamp.far_plane == cur_state.depth_clamp.far_plane &&
506 depth_clamp.near_plane == cur_state.depth_clamp.near_plane) {
507 return;
508 }
509 UNIMPLEMENTED_IF_MSG(depth_clamp.far_plane != depth_clamp.near_plane,
510 "Unimplemented Depth Clamp Separation!");
511
512 if (depth_clamp.far_plane || depth_clamp.near_plane) {
513 glEnable(GL_DEPTH_CLAMP);
514 } else {
515 glDisable(GL_DEPTH_CLAMP);
516 }
517}
518
519void OpenGLState::Apply() const { 512void OpenGLState::Apply() const {
520 ApplyFramebufferState(); 513 ApplyFramebufferState();
521 ApplyVertexArrayState(); 514 ApplyVertexArrayState();
522 515 ApplyShaderProgram();
523 // Shader program 516 ApplyProgramPipeline();
524 if (draw.shader_program != cur_state.draw.shader_program) { 517 ApplyClipDistances();
525 glUseProgram(draw.shader_program); 518 ApplyPointSize();
526 } 519 ApplyFragmentColorClamp();
527 520 ApplyMultisample();
528 // Program pipeline
529 if (draw.program_pipeline != cur_state.draw.program_pipeline) {
530 glBindProgramPipeline(draw.program_pipeline);
531 }
532 // Clip distance
533 for (std::size_t i = 0; i < clip_distance.size(); ++i) {
534 if (clip_distance[i] != cur_state.clip_distance[i]) {
535 if (clip_distance[i]) {
536 glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i));
537 } else {
538 glDisable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i));
539 }
540 }
541 }
542 // Point
543 if (point.size != cur_state.point.size) {
544 glPointSize(point.size);
545 }
546 if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) {
547 glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
548 fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
549 }
550 if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) {
551 if (multisample_control.alpha_to_coverage) {
552 glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
553 } else {
554 glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
555 }
556 }
557 if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) {
558 if (multisample_control.alpha_to_one) {
559 glEnable(GL_SAMPLE_ALPHA_TO_ONE);
560 } else {
561 glDisable(GL_SAMPLE_ALPHA_TO_ONE);
562 }
563 }
564 ApplyDepthClamp(); 521 ApplyDepthClamp();
565 ApplyColorMask(); 522 ApplyColorMask();
566 ApplyViewport(); 523 ApplyViewport();
@@ -574,7 +531,28 @@ void OpenGLState::Apply() const {
574 ApplyTextures(); 531 ApplyTextures();
575 ApplySamplers(); 532 ApplySamplers();
576 ApplyPolygonOffset(); 533 ApplyPolygonOffset();
577 cur_state = *this; 534}
535
536void OpenGLState::EmulateViewportWithScissor() {
537 auto& current = viewports[0];
538 if (current.scissor.enabled) {
539 const GLint left = std::max(current.x, current.scissor.x);
540 const GLint right =
541 std::max(current.x + current.width, current.scissor.x + current.scissor.width);
542 const GLint bottom = std::max(current.y, current.scissor.y);
543 const GLint top =
544 std::max(current.y + current.height, current.scissor.y + current.scissor.height);
545 current.scissor.x = std::max(left, 0);
546 current.scissor.y = std::max(bottom, 0);
547 current.scissor.width = std::max(right - left, 0);
548 current.scissor.height = std::max(top - bottom, 0);
549 } else {
550 current.scissor.enabled = true;
551 current.scissor.x = current.x;
552 current.scissor.y = current.y;
553 current.scissor.width = current.width;
554 current.scissor.height = current.height;
555 }
578} 556}
579 557
580OpenGLState& OpenGLState::UnbindTexture(GLuint handle) { 558OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 9e1eda5b1..41418a7b8 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -54,10 +54,6 @@ public:
54 } depth_clamp; // GL_DEPTH_CLAMP 54 } depth_clamp; // GL_DEPTH_CLAMP
55 55
56 struct { 56 struct {
57 bool enabled; // viewports arrays are only supported when geometry shaders are enabled.
58 } geometry_shaders;
59
60 struct {
61 bool enabled; // GL_CULL_FACE 57 bool enabled; // GL_CULL_FACE
62 GLenum mode; // GL_CULL_FACE_MODE 58 GLenum mode; // GL_CULL_FACE_MODE
63 GLenum front_face; // GL_FRONT_FACE 59 GLenum front_face; // GL_FRONT_FACE
@@ -184,34 +180,26 @@ public:
184 static OpenGLState GetCurState() { 180 static OpenGLState GetCurState() {
185 return cur_state; 181 return cur_state;
186 } 182 }
183
187 static bool GetsRGBUsed() { 184 static bool GetsRGBUsed() {
188 return s_rgb_used; 185 return s_rgb_used;
189 } 186 }
187
190 static void ClearsRGBUsed() { 188 static void ClearsRGBUsed() {
191 s_rgb_used = false; 189 s_rgb_used = false;
192 } 190 }
191
193 /// Apply this state as the current OpenGL state 192 /// Apply this state as the current OpenGL state
194 void Apply() const; 193 void Apply() const;
195 /// Apply only the state affecting the framebuffer 194
196 void ApplyFramebufferState() const; 195 void ApplyFramebufferState() const;
197 /// Apply only the state affecting the vertex array
198 void ApplyVertexArrayState() const; 196 void ApplyVertexArrayState() const;
199 /// Set the initial OpenGL state 197 void ApplyShaderProgram() const;
200 static void ApplyDefaultState(); 198 void ApplyProgramPipeline() const;
201 /// Resets any references to the given resource 199 void ApplyClipDistances() const;
202 OpenGLState& UnbindTexture(GLuint handle); 200 void ApplyPointSize() const;
203 OpenGLState& ResetSampler(GLuint handle); 201 void ApplyFragmentColorClamp() const;
204 OpenGLState& ResetProgram(GLuint handle); 202 void ApplyMultisample() const;
205 OpenGLState& ResetPipeline(GLuint handle);
206 OpenGLState& ResetVertexArray(GLuint handle);
207 OpenGLState& ResetFramebuffer(GLuint handle);
208 void EmulateViewportWithScissor();
209
210private:
211 static OpenGLState cur_state;
212 // Workaround for sRGB problems caused by
213 // QT not supporting srgb output
214 static bool s_rgb_used;
215 void ApplySRgb() const; 203 void ApplySRgb() const;
216 void ApplyCulling() const; 204 void ApplyCulling() const;
217 void ApplyColorMask() const; 205 void ApplyColorMask() const;
@@ -227,6 +215,26 @@ private:
227 void ApplySamplers() const; 215 void ApplySamplers() const;
228 void ApplyDepthClamp() const; 216 void ApplyDepthClamp() const;
229 void ApplyPolygonOffset() const; 217 void ApplyPolygonOffset() const;
218
219 /// Set the initial OpenGL state
220 static void ApplyDefaultState();
221
222 /// Resets any references to the given resource
223 OpenGLState& UnbindTexture(GLuint handle);
224 OpenGLState& ResetSampler(GLuint handle);
225 OpenGLState& ResetProgram(GLuint handle);
226 OpenGLState& ResetPipeline(GLuint handle);
227 OpenGLState& ResetVertexArray(GLuint handle);
228 OpenGLState& ResetFramebuffer(GLuint handle);
229
230 /// Viewport does not affects glClearBuffer so emulate viewport using scissor test
231 void EmulateViewportWithScissor();
232
233private:
234 static OpenGLState cur_state;
235
236 // Workaround for sRGB problems caused by QT not supporting srgb output
237 static bool s_rgb_used;
230}; 238};
231 239
232} // namespace OpenGL 240} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index a01efeb05..d69cba9c3 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -5,7 +5,6 @@
5#include <algorithm> 5#include <algorithm>
6#include <cstddef> 6#include <cstddef>
7#include <cstdlib> 7#include <cstdlib>
8#include <cstring>
9#include <memory> 8#include <memory>
10#include <glad/glad.h> 9#include <glad/glad.h>
11#include "common/assert.h" 10#include "common/assert.h"
diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp
index d84634cb3..84a987371 100644
--- a/src/video_core/renderer_opengl/utils.cpp
+++ b/src/video_core/renderer_opengl/utils.cpp
@@ -5,11 +5,39 @@
5#include <string> 5#include <string>
6#include <fmt/format.h> 6#include <fmt/format.h>
7#include <glad/glad.h> 7#include <glad/glad.h>
8#include "common/assert.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "video_core/renderer_opengl/utils.h" 10#include "video_core/renderer_opengl/utils.h"
10 11
11namespace OpenGL { 12namespace OpenGL {
12 13
14BindBuffersRangePushBuffer::BindBuffersRangePushBuffer(GLenum target) : target{target} {}
15
16BindBuffersRangePushBuffer::~BindBuffersRangePushBuffer() = default;
17
18void BindBuffersRangePushBuffer::Setup(GLuint first_) {
19 first = first_;
20 buffers.clear();
21 offsets.clear();
22 sizes.clear();
23}
24
25void BindBuffersRangePushBuffer::Push(GLuint buffer, GLintptr offset, GLsizeiptr size) {
26 buffers.push_back(buffer);
27 offsets.push_back(offset);
28 sizes.push_back(size);
29}
30
31void BindBuffersRangePushBuffer::Bind() const {
32 const std::size_t count{buffers.size()};
33 DEBUG_ASSERT(count == offsets.size() && count == sizes.size());
34 if (count == 0) {
35 return;
36 }
37 glBindBuffersRange(target, first, static_cast<GLsizei>(count), buffers.data(), offsets.data(),
38 sizes.data());
39}
40
13void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) { 41void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) {
14 if (!GLAD_GL_KHR_debug) { 42 if (!GLAD_GL_KHR_debug) {
15 return; // We don't need to throw an error as this is just for debugging 43 return; // We don't need to throw an error as this is just for debugging
diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h
index 1fcb6fc11..aef45c9dc 100644
--- a/src/video_core/renderer_opengl/utils.h
+++ b/src/video_core/renderer_opengl/utils.h
@@ -5,11 +5,31 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include <vector>
8#include <glad/glad.h> 9#include <glad/glad.h>
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
11namespace OpenGL { 12namespace OpenGL {
12 13
14class BindBuffersRangePushBuffer {
15public:
16 BindBuffersRangePushBuffer(GLenum target);
17 ~BindBuffersRangePushBuffer();
18
19 void Setup(GLuint first_);
20
21 void Push(GLuint buffer, GLintptr offset, GLsizeiptr size);
22
23 void Bind() const;
24
25private:
26 GLenum target;
27 GLuint first;
28 std::vector<GLuint> buffers;
29 std::vector<GLintptr> offsets;
30 std::vector<GLsizeiptr> sizes;
31};
32
13void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = ""); 33void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = "");
14 34
15} // namespace OpenGL \ No newline at end of file 35} // namespace OpenGL \ No newline at end of file
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 388b5ffd5..02a9f5ecb 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -10,6 +10,7 @@
10#include "common/alignment.h" 10#include "common/alignment.h"
11#include "common/assert.h" 11#include "common/assert.h"
12#include "core/memory.h" 12#include "core/memory.h"
13#include "video_core/memory_manager.h"
13#include "video_core/renderer_vulkan/declarations.h" 14#include "video_core/renderer_vulkan/declarations.h"
14#include "video_core/renderer_vulkan/vk_buffer_cache.h" 15#include "video_core/renderer_vulkan/vk_buffer_cache.h"
15#include "video_core/renderer_vulkan/vk_scheduler.h" 16#include "video_core/renderer_vulkan/vk_scheduler.h"
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
new file mode 100644
index 000000000..25500f9a3
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -0,0 +1,1381 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <functional>
6#include <map>
7#include <set>
8
9#include <fmt/format.h>
10
11#include <sirit/sirit.h>
12
13#include "common/alignment.h"
14#include "common/assert.h"
15#include "common/common_types.h"
16#include "common/logging/log.h"
17#include "video_core/engines/maxwell_3d.h"
18#include "video_core/engines/shader_bytecode.h"
19#include "video_core/engines/shader_header.h"
20#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
21#include "video_core/shader/shader_ir.h"
22
23namespace Vulkan::VKShader {
24
25using Sirit::Id;
26using Tegra::Shader::Attribute;
27using Tegra::Shader::AttributeUse;
28using Tegra::Shader::Register;
29using namespace VideoCommon::Shader;
30
31using Maxwell = Tegra::Engines::Maxwell3D::Regs;
32using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage;
33using Operation = const OperationNode&;
34
35// TODO(Rodrigo): Use rasterizer's value
36constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 0x1000;
37constexpr u32 STAGE_BINDING_STRIDE = 0x100;
38
39enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat };
40
41struct SamplerImage {
42 Id image_type;
43 Id sampled_image_type;
44 Id sampler;
45};
46
47namespace {
48
49spv::Dim GetSamplerDim(const Sampler& sampler) {
50 switch (sampler.GetType()) {
51 case Tegra::Shader::TextureType::Texture1D:
52 return spv::Dim::Dim1D;
53 case Tegra::Shader::TextureType::Texture2D:
54 return spv::Dim::Dim2D;
55 case Tegra::Shader::TextureType::Texture3D:
56 return spv::Dim::Dim3D;
57 case Tegra::Shader::TextureType::TextureCube:
58 return spv::Dim::Cube;
59 default:
60 UNIMPLEMENTED_MSG("Unimplemented sampler type={}", static_cast<u32>(sampler.GetType()));
61 return spv::Dim::Dim2D;
62 }
63}
64
65/// Returns true if an attribute index is one of the 32 generic attributes
66constexpr bool IsGenericAttribute(Attribute::Index attribute) {
67 return attribute >= Attribute::Index::Attribute_0 &&
68 attribute <= Attribute::Index::Attribute_31;
69}
70
71/// Returns the location of a generic attribute
72constexpr u32 GetGenericAttributeLocation(Attribute::Index attribute) {
73 ASSERT(IsGenericAttribute(attribute));
74 return static_cast<u32>(attribute) - static_cast<u32>(Attribute::Index::Attribute_0);
75}
76
77/// Returns true if an object has to be treated as precise
78bool IsPrecise(Operation operand) {
79 const auto& meta = operand.GetMeta();
80
81 if (std::holds_alternative<MetaArithmetic>(meta)) {
82 return std::get<MetaArithmetic>(meta).precise;
83 }
84 if (std::holds_alternative<MetaHalfArithmetic>(meta)) {
85 return std::get<MetaHalfArithmetic>(meta).precise;
86 }
87 return false;
88}
89
90} // namespace
91
92class SPIRVDecompiler : public Sirit::Module {
93public:
94 explicit SPIRVDecompiler(const ShaderIR& ir, ShaderStage stage)
95 : Module(0x00010300), ir{ir}, stage{stage}, header{ir.GetHeader()} {
96 AddCapability(spv::Capability::Shader);
97 AddExtension("SPV_KHR_storage_buffer_storage_class");
98 AddExtension("SPV_KHR_variable_pointers");
99 }
100
101 void Decompile() {
102 AllocateBindings();
103 AllocateLabels();
104
105 DeclareVertex();
106 DeclareGeometry();
107 DeclareFragment();
108 DeclareRegisters();
109 DeclarePredicates();
110 DeclareLocalMemory();
111 DeclareInternalFlags();
112 DeclareInputAttributes();
113 DeclareOutputAttributes();
114 DeclareConstantBuffers();
115 DeclareGlobalBuffers();
116 DeclareSamplers();
117
118 execute_function =
119 Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void)));
120 Emit(OpLabel());
121
122 const u32 first_address = ir.GetBasicBlocks().begin()->first;
123 const Id loop_label = OpLabel("loop");
124 const Id merge_label = OpLabel("merge");
125 const Id dummy_label = OpLabel();
126 const Id jump_label = OpLabel();
127 continue_label = OpLabel("continue");
128
129 std::vector<Sirit::Literal> literals;
130 std::vector<Id> branch_labels;
131 for (const auto& pair : labels) {
132 const auto [literal, label] = pair;
133 literals.push_back(literal);
134 branch_labels.push_back(label);
135 }
136
137 // TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely
138 // that shaders will use 20 nested SSYs and PBKs.
139 constexpr u32 FLOW_STACK_SIZE = 20;
140 const Id flow_stack_type = TypeArray(t_uint, Constant(t_uint, FLOW_STACK_SIZE));
141 jmp_to = Emit(OpVariable(TypePointer(spv::StorageClass::Function, t_uint),
142 spv::StorageClass::Function, Constant(t_uint, first_address)));
143 flow_stack = Emit(OpVariable(TypePointer(spv::StorageClass::Function, flow_stack_type),
144 spv::StorageClass::Function, ConstantNull(flow_stack_type)));
145 flow_stack_top =
146 Emit(OpVariable(t_func_uint, spv::StorageClass::Function, Constant(t_uint, 0)));
147
148 Name(jmp_to, "jmp_to");
149 Name(flow_stack, "flow_stack");
150 Name(flow_stack_top, "flow_stack_top");
151
152 Emit(OpBranch(loop_label));
153 Emit(loop_label);
154 Emit(OpLoopMerge(merge_label, continue_label, spv::LoopControlMask::Unroll));
155 Emit(OpBranch(dummy_label));
156
157 Emit(dummy_label);
158 const Id default_branch = OpLabel();
159 const Id jmp_to_load = Emit(OpLoad(t_uint, jmp_to));
160 Emit(OpSelectionMerge(jump_label, spv::SelectionControlMask::MaskNone));
161 Emit(OpSwitch(jmp_to_load, default_branch, literals, branch_labels));
162
163 Emit(default_branch);
164 Emit(OpReturn());
165
166 for (const auto& pair : ir.GetBasicBlocks()) {
167 const auto& [address, bb] = pair;
168 Emit(labels.at(address));
169
170 VisitBasicBlock(bb);
171
172 const auto next_it = labels.lower_bound(address + 1);
173 const Id next_label = next_it != labels.end() ? next_it->second : default_branch;
174 Emit(OpBranch(next_label));
175 }
176
177 Emit(jump_label);
178 Emit(OpBranch(continue_label));
179 Emit(continue_label);
180 Emit(OpBranch(loop_label));
181 Emit(merge_label);
182 Emit(OpReturn());
183 Emit(OpFunctionEnd());
184 }
185
186 ShaderEntries GetShaderEntries() const {
187 ShaderEntries entries;
188 entries.const_buffers_base_binding = const_buffers_base_binding;
189 entries.global_buffers_base_binding = global_buffers_base_binding;
190 entries.samplers_base_binding = samplers_base_binding;
191 for (const auto& cbuf : ir.GetConstantBuffers()) {
192 entries.const_buffers.emplace_back(cbuf.second, cbuf.first);
193 }
194 for (const auto& gmem_pair : ir.GetGlobalMemory()) {
195 const auto& [base, usage] = gmem_pair;
196 entries.global_buffers.emplace_back(base.cbuf_index, base.cbuf_offset);
197 }
198 for (const auto& sampler : ir.GetSamplers()) {
199 entries.samplers.emplace_back(sampler);
200 }
201 for (const auto& attr : ir.GetInputAttributes()) {
202 entries.attributes.insert(GetGenericAttributeLocation(attr.first));
203 }
204 entries.clip_distances = ir.GetClipDistances();
205 entries.shader_length = ir.GetLength();
206 entries.entry_function = execute_function;
207 entries.interfaces = interfaces;
208 return entries;
209 }
210
211private:
212 using OperationDecompilerFn = Id (SPIRVDecompiler::*)(Operation);
213 using OperationDecompilersArray =
214 std::array<OperationDecompilerFn, static_cast<std::size_t>(OperationCode::Amount)>;
215
216 static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount);
217 static constexpr u32 CBUF_STRIDE = 16;
218
219 void AllocateBindings() {
220 const u32 binding_base = static_cast<u32>(stage) * STAGE_BINDING_STRIDE;
221 u32 binding_iterator = binding_base;
222
223 const auto Allocate = [&binding_iterator](std::size_t count) {
224 const u32 current_binding = binding_iterator;
225 binding_iterator += static_cast<u32>(count);
226 return current_binding;
227 };
228 const_buffers_base_binding = Allocate(ir.GetConstantBuffers().size());
229 global_buffers_base_binding = Allocate(ir.GetGlobalMemory().size());
230 samplers_base_binding = Allocate(ir.GetSamplers().size());
231
232 ASSERT_MSG(binding_iterator - binding_base < STAGE_BINDING_STRIDE,
233 "Stage binding stride is too small");
234 }
235
236 void AllocateLabels() {
237 for (const auto& pair : ir.GetBasicBlocks()) {
238 const u32 address = pair.first;
239 labels.emplace(address, OpLabel(fmt::format("label_0x{:x}", address)));
240 }
241 }
242
243 void DeclareVertex() {
244 if (stage != ShaderStage::Vertex)
245 return;
246
247 DeclareVertexRedeclarations();
248 }
249
250 void DeclareGeometry() {
251 if (stage != ShaderStage::Geometry)
252 return;
253
254 UNIMPLEMENTED();
255 }
256
257 void DeclareFragment() {
258 if (stage != ShaderStage::Fragment)
259 return;
260
261 for (u32 rt = 0; rt < static_cast<u32>(frag_colors.size()); ++rt) {
262 if (!IsRenderTargetUsed(rt)) {
263 continue;
264 }
265
266 const Id id = AddGlobalVariable(OpVariable(t_out_float4, spv::StorageClass::Output));
267 Name(id, fmt::format("frag_color{}", rt));
268 Decorate(id, spv::Decoration::Location, rt);
269
270 frag_colors[rt] = id;
271 interfaces.push_back(id);
272 }
273
274 if (header.ps.omap.depth) {
275 frag_depth = AddGlobalVariable(OpVariable(t_out_float, spv::StorageClass::Output));
276 Name(frag_depth, "frag_depth");
277 Decorate(frag_depth, spv::Decoration::BuiltIn,
278 static_cast<u32>(spv::BuiltIn::FragDepth));
279
280 interfaces.push_back(frag_depth);
281 }
282
283 frag_coord = DeclareBuiltIn(spv::BuiltIn::FragCoord, spv::StorageClass::Input, t_in_float4,
284 "frag_coord");
285 front_facing = DeclareBuiltIn(spv::BuiltIn::FrontFacing, spv::StorageClass::Input,
286 t_in_bool, "front_facing");
287 }
288
289 void DeclareRegisters() {
290 for (const u32 gpr : ir.GetRegisters()) {
291 const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero);
292 Name(id, fmt::format("gpr_{}", gpr));
293 registers.emplace(gpr, AddGlobalVariable(id));
294 }
295 }
296
297 void DeclarePredicates() {
298 for (const auto pred : ir.GetPredicates()) {
299 const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
300 Name(id, fmt::format("pred_{}", static_cast<u32>(pred)));
301 predicates.emplace(pred, AddGlobalVariable(id));
302 }
303 }
304
305 void DeclareLocalMemory() {
306 if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) {
307 const auto element_count = static_cast<u32>(Common::AlignUp(local_memory_size, 4) / 4);
308 const Id type_array = TypeArray(t_float, Constant(t_uint, element_count));
309 const Id type_pointer = TypePointer(spv::StorageClass::Private, type_array);
310 Name(type_pointer, "LocalMemory");
311
312 local_memory =
313 OpVariable(type_pointer, spv::StorageClass::Private, ConstantNull(type_array));
314 AddGlobalVariable(Name(local_memory, "local_memory"));
315 }
316 }
317
318 void DeclareInternalFlags() {
319 constexpr std::array<const char*, INTERNAL_FLAGS_COUNT> names = {"zero", "sign", "carry",
320 "overflow"};
321 for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) {
322 const auto flag_code = static_cast<InternalFlag>(flag);
323 const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
324 internal_flags[flag] = AddGlobalVariable(Name(id, names[flag]));
325 }
326 }
327
328 void DeclareInputAttributes() {
329 for (const auto element : ir.GetInputAttributes()) {
330 const Attribute::Index index = element.first;
331 if (!IsGenericAttribute(index)) {
332 continue;
333 }
334
335 UNIMPLEMENTED_IF(stage == ShaderStage::Geometry);
336
337 const u32 location = GetGenericAttributeLocation(index);
338 const Id id = OpVariable(t_in_float4, spv::StorageClass::Input);
339 Name(AddGlobalVariable(id), fmt::format("in_attr{}", location));
340 input_attributes.emplace(index, id);
341 interfaces.push_back(id);
342
343 Decorate(id, spv::Decoration::Location, location);
344
345 if (stage != ShaderStage::Fragment) {
346 continue;
347 }
348 switch (header.ps.GetAttributeUse(location)) {
349 case AttributeUse::Constant:
350 Decorate(id, spv::Decoration::Flat);
351 break;
352 case AttributeUse::ScreenLinear:
353 Decorate(id, spv::Decoration::NoPerspective);
354 break;
355 case AttributeUse::Perspective:
356 // Default
357 break;
358 default:
359 UNREACHABLE_MSG("Unused attribute being fetched");
360 }
361 }
362 }
363
364 void DeclareOutputAttributes() {
365 for (const auto index : ir.GetOutputAttributes()) {
366 if (!IsGenericAttribute(index)) {
367 continue;
368 }
369 const auto location = GetGenericAttributeLocation(index);
370 const Id id = OpVariable(t_out_float4, spv::StorageClass::Output);
371 Name(AddGlobalVariable(id), fmt::format("out_attr{}", location));
372 output_attributes.emplace(index, id);
373 interfaces.push_back(id);
374
375 Decorate(id, spv::Decoration::Location, location);
376 }
377 }
378
379 void DeclareConstantBuffers() {
380 u32 binding = const_buffers_base_binding;
381 for (const auto& entry : ir.GetConstantBuffers()) {
382 const auto [index, size] = entry;
383 const Id id = OpVariable(t_cbuf_ubo, spv::StorageClass::Uniform);
384 AddGlobalVariable(Name(id, fmt::format("cbuf_{}", index)));
385
386 Decorate(id, spv::Decoration::Binding, binding++);
387 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
388 constant_buffers.emplace(index, id);
389 }
390 }
391
392 void DeclareGlobalBuffers() {
393 u32 binding = global_buffers_base_binding;
394 for (const auto& entry : ir.GetGlobalMemory()) {
395 const auto [base, usage] = entry;
396 const Id id = OpVariable(t_gmem_ssbo, spv::StorageClass::StorageBuffer);
397 AddGlobalVariable(
398 Name(id, fmt::format("gmem_{}_{}", base.cbuf_index, base.cbuf_offset)));
399
400 Decorate(id, spv::Decoration::Binding, binding++);
401 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
402 global_buffers.emplace(base, id);
403 }
404 }
405
406 void DeclareSamplers() {
407 u32 binding = samplers_base_binding;
408 for (const auto& sampler : ir.GetSamplers()) {
409 const auto dim = GetSamplerDim(sampler);
410 const int depth = sampler.IsShadow() ? 1 : 0;
411 const int arrayed = sampler.IsArray() ? 1 : 0;
412 // TODO(Rodrigo): Sampled 1 indicates that the image will be used with a sampler. When
413 // SULD and SUST instructions are implemented, replace this value.
414 const int sampled = 1;
415 const Id image_type =
416 TypeImage(t_float, dim, depth, arrayed, false, sampled, spv::ImageFormat::Unknown);
417 const Id sampled_image_type = TypeSampledImage(image_type);
418 const Id pointer_type =
419 TypePointer(spv::StorageClass::UniformConstant, sampled_image_type);
420 const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant);
421 AddGlobalVariable(Name(id, fmt::format("sampler_{}", sampler.GetIndex())));
422
423 sampler_images.insert(
424 {static_cast<u32>(sampler.GetIndex()), {image_type, sampled_image_type, id}});
425
426 Decorate(id, spv::Decoration::Binding, binding++);
427 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
428 }
429 }
430
431 void DeclareVertexRedeclarations() {
432 vertex_index = DeclareBuiltIn(spv::BuiltIn::VertexIndex, spv::StorageClass::Input,
433 t_in_uint, "vertex_index");
434 instance_index = DeclareBuiltIn(spv::BuiltIn::InstanceIndex, spv::StorageClass::Input,
435 t_in_uint, "instance_index");
436
437 bool is_point_size_declared = false;
438 bool is_clip_distances_declared = false;
439 for (const auto index : ir.GetOutputAttributes()) {
440 if (index == Attribute::Index::PointSize) {
441 is_point_size_declared = true;
442 } else if (index == Attribute::Index::ClipDistances0123 ||
443 index == Attribute::Index::ClipDistances4567) {
444 is_clip_distances_declared = true;
445 }
446 }
447
448 std::vector<Id> members;
449 members.push_back(t_float4);
450 if (is_point_size_declared) {
451 members.push_back(t_float);
452 }
453 if (is_clip_distances_declared) {
454 members.push_back(TypeArray(t_float, Constant(t_uint, 8)));
455 }
456
457 const Id gl_per_vertex_struct = Name(TypeStruct(members), "PerVertex");
458 Decorate(gl_per_vertex_struct, spv::Decoration::Block);
459
460 u32 declaration_index = 0;
461 const auto MemberDecorateBuiltIn = [&](spv::BuiltIn builtin, std::string name,
462 bool condition) {
463 if (!condition)
464 return u32{};
465 MemberName(gl_per_vertex_struct, declaration_index, name);
466 MemberDecorate(gl_per_vertex_struct, declaration_index, spv::Decoration::BuiltIn,
467 static_cast<u32>(builtin));
468 return declaration_index++;
469 };
470
471 position_index = MemberDecorateBuiltIn(spv::BuiltIn::Position, "position", true);
472 point_size_index =
473 MemberDecorateBuiltIn(spv::BuiltIn::PointSize, "point_size", is_point_size_declared);
474 clip_distances_index = MemberDecorateBuiltIn(spv::BuiltIn::ClipDistance, "clip_distances",
475 is_clip_distances_declared);
476
477 const Id type_pointer = TypePointer(spv::StorageClass::Output, gl_per_vertex_struct);
478 per_vertex = OpVariable(type_pointer, spv::StorageClass::Output);
479 AddGlobalVariable(Name(per_vertex, "per_vertex"));
480 interfaces.push_back(per_vertex);
481 }
482
483 void VisitBasicBlock(const NodeBlock& bb) {
484 for (const Node node : bb) {
485 static_cast<void>(Visit(node));
486 }
487 }
488
489 Id Visit(Node node) {
490 if (const auto operation = std::get_if<OperationNode>(node)) {
491 const auto operation_index = static_cast<std::size_t>(operation->GetCode());
492 const auto decompiler = operation_decompilers[operation_index];
493 if (decompiler == nullptr) {
494 UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index);
495 }
496 return (this->*decompiler)(*operation);
497
498 } else if (const auto gpr = std::get_if<GprNode>(node)) {
499 const u32 index = gpr->GetIndex();
500 if (index == Register::ZeroIndex) {
501 return Constant(t_float, 0.0f);
502 }
503 return Emit(OpLoad(t_float, registers.at(index)));
504
505 } else if (const auto immediate = std::get_if<ImmediateNode>(node)) {
506 return BitcastTo<Type::Float>(Constant(t_uint, immediate->GetValue()));
507
508 } else if (const auto predicate = std::get_if<PredicateNode>(node)) {
509 const auto value = [&]() -> Id {
510 switch (const auto index = predicate->GetIndex(); index) {
511 case Tegra::Shader::Pred::UnusedIndex:
512 return v_true;
513 case Tegra::Shader::Pred::NeverExecute:
514 return v_false;
515 default:
516 return Emit(OpLoad(t_bool, predicates.at(index)));
517 }
518 }();
519 if (predicate->IsNegated()) {
520 return Emit(OpLogicalNot(t_bool, value));
521 }
522 return value;
523
524 } else if (const auto abuf = std::get_if<AbufNode>(node)) {
525 const auto attribute = abuf->GetIndex();
526 const auto element = abuf->GetElement();
527
528 switch (attribute) {
529 case Attribute::Index::Position:
530 if (stage != ShaderStage::Fragment) {
531 UNIMPLEMENTED();
532 break;
533 } else {
534 if (element == 3) {
535 return Constant(t_float, 1.0f);
536 }
537 return Emit(OpLoad(t_float, AccessElement(t_in_float, frag_coord, element)));
538 }
539 case Attribute::Index::TessCoordInstanceIDVertexID:
540 // TODO(Subv): Find out what the values are for the first two elements when inside a
541 // vertex shader, and what's the value of the fourth element when inside a Tess Eval
542 // shader.
543 ASSERT(stage == ShaderStage::Vertex);
544 switch (element) {
545 case 2:
546 return BitcastFrom<Type::Uint>(Emit(OpLoad(t_uint, instance_index)));
547 case 3:
548 return BitcastFrom<Type::Uint>(Emit(OpLoad(t_uint, vertex_index)));
549 }
550 UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
551 return Constant(t_float, 0);
552 case Attribute::Index::FrontFacing:
553 // TODO(Subv): Find out what the values are for the other elements.
554 ASSERT(stage == ShaderStage::Fragment);
555 if (element == 3) {
556 const Id is_front_facing = Emit(OpLoad(t_bool, front_facing));
557 const Id true_value =
558 BitcastTo<Type::Float>(Constant(t_int, static_cast<s32>(-1)));
559 const Id false_value = BitcastTo<Type::Float>(Constant(t_int, 0));
560 return Emit(OpSelect(t_float, is_front_facing, true_value, false_value));
561 }
562 UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
563 return Constant(t_float, 0.0f);
564 default:
565 if (IsGenericAttribute(attribute)) {
566 const Id pointer =
567 AccessElement(t_in_float, input_attributes.at(attribute), element);
568 return Emit(OpLoad(t_float, pointer));
569 }
570 break;
571 }
572 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
573
574 } else if (const auto cbuf = std::get_if<CbufNode>(node)) {
575 const Node offset = cbuf->GetOffset();
576 const Id buffer_id = constant_buffers.at(cbuf->GetIndex());
577
578 Id buffer_index{};
579 Id buffer_element{};
580
581 if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
582 // Direct access
583 const u32 offset_imm = immediate->GetValue();
584 ASSERT(offset_imm % 4 == 0);
585 buffer_index = Constant(t_uint, offset_imm / 16);
586 buffer_element = Constant(t_uint, (offset_imm / 4) % 4);
587
588 } else if (std::holds_alternative<OperationNode>(*offset)) {
589 // Indirect access
590 // TODO(Rodrigo): Use a uniform buffer stride of 4 and drop this slow math (which
591 // emits sub-optimal code on GLSL from my testing).
592 const Id offset_id = BitcastTo<Type::Uint>(Visit(offset));
593 const Id unsafe_offset = Emit(OpUDiv(t_uint, offset_id, Constant(t_uint, 4)));
594 const Id final_offset = Emit(
595 OpUMod(t_uint, unsafe_offset, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS - 1)));
596 buffer_index = Emit(OpUDiv(t_uint, final_offset, Constant(t_uint, 4)));
597 buffer_element = Emit(OpUMod(t_uint, final_offset, Constant(t_uint, 4)));
598
599 } else {
600 UNREACHABLE_MSG("Unmanaged offset node type");
601 }
602
603 const Id pointer = Emit(OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0),
604 buffer_index, buffer_element));
605 return Emit(OpLoad(t_float, pointer));
606
607 } else if (const auto gmem = std::get_if<GmemNode>(node)) {
608 const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor());
609 const Id real = BitcastTo<Type::Uint>(Visit(gmem->GetRealAddress()));
610 const Id base = BitcastTo<Type::Uint>(Visit(gmem->GetBaseAddress()));
611
612 Id offset = Emit(OpISub(t_uint, real, base));
613 offset = Emit(OpUDiv(t_uint, offset, Constant(t_uint, 4u)));
614 return Emit(OpLoad(t_float, Emit(OpAccessChain(t_gmem_float, gmem_buffer,
615 Constant(t_uint, 0u), offset))));
616
617 } else if (const auto conditional = std::get_if<ConditionalNode>(node)) {
618 // It's invalid to call conditional on nested nodes, use an operation instead
619 const Id true_label = OpLabel();
620 const Id skip_label = OpLabel();
621 Emit(OpBranchConditional(Visit(conditional->GetCondition()), true_label, skip_label));
622 Emit(true_label);
623
624 VisitBasicBlock(conditional->GetCode());
625
626 Emit(OpBranch(skip_label));
627 Emit(skip_label);
628 return {};
629
630 } else if (const auto comment = std::get_if<CommentNode>(node)) {
631 Name(Emit(OpUndef(t_void)), comment->GetText());
632 return {};
633 }
634
635 UNREACHABLE();
636 return {};
637 }
638
639 template <Id (Module::*func)(Id, Id), Type result_type, Type type_a = result_type>
640 Id Unary(Operation operation) {
641 const Id type_def = GetTypeDefinition(result_type);
642 const Id op_a = VisitOperand<type_a>(operation, 0);
643
644 const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a)));
645 if (IsPrecise(operation)) {
646 Decorate(value, spv::Decoration::NoContraction);
647 }
648 return value;
649 }
650
651 template <Id (Module::*func)(Id, Id, Id), Type result_type, Type type_a = result_type,
652 Type type_b = type_a>
653 Id Binary(Operation operation) {
654 const Id type_def = GetTypeDefinition(result_type);
655 const Id op_a = VisitOperand<type_a>(operation, 0);
656 const Id op_b = VisitOperand<type_b>(operation, 1);
657
658 const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b)));
659 if (IsPrecise(operation)) {
660 Decorate(value, spv::Decoration::NoContraction);
661 }
662 return value;
663 }
664
665 template <Id (Module::*func)(Id, Id, Id, Id), Type result_type, Type type_a = result_type,
666 Type type_b = type_a, Type type_c = type_b>
667 Id Ternary(Operation operation) {
668 const Id type_def = GetTypeDefinition(result_type);
669 const Id op_a = VisitOperand<type_a>(operation, 0);
670 const Id op_b = VisitOperand<type_b>(operation, 1);
671 const Id op_c = VisitOperand<type_c>(operation, 2);
672
673 const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b, op_c)));
674 if (IsPrecise(operation)) {
675 Decorate(value, spv::Decoration::NoContraction);
676 }
677 return value;
678 }
679
680 template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type, Type type_a = result_type,
681 Type type_b = type_a, Type type_c = type_b, Type type_d = type_c>
682 Id Quaternary(Operation operation) {
683 const Id type_def = GetTypeDefinition(result_type);
684 const Id op_a = VisitOperand<type_a>(operation, 0);
685 const Id op_b = VisitOperand<type_b>(operation, 1);
686 const Id op_c = VisitOperand<type_c>(operation, 2);
687 const Id op_d = VisitOperand<type_d>(operation, 3);
688
689 const Id value =
690 BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b, op_c, op_d)));
691 if (IsPrecise(operation)) {
692 Decorate(value, spv::Decoration::NoContraction);
693 }
694 return value;
695 }
696
697 Id Assign(Operation operation) {
698 const Node dest = operation[0];
699 const Node src = operation[1];
700
701 Id target{};
702 if (const auto gpr = std::get_if<GprNode>(dest)) {
703 if (gpr->GetIndex() == Register::ZeroIndex) {
704 // Writing to Register::ZeroIndex is a no op
705 return {};
706 }
707 target = registers.at(gpr->GetIndex());
708
709 } else if (const auto abuf = std::get_if<AbufNode>(dest)) {
710 target = [&]() -> Id {
711 switch (const auto attribute = abuf->GetIndex(); attribute) {
712 case Attribute::Index::Position:
713 return AccessElement(t_out_float, per_vertex, position_index,
714 abuf->GetElement());
715 case Attribute::Index::PointSize:
716 return AccessElement(t_out_float, per_vertex, point_size_index);
717 case Attribute::Index::ClipDistances0123:
718 return AccessElement(t_out_float, per_vertex, clip_distances_index,
719 abuf->GetElement());
720 case Attribute::Index::ClipDistances4567:
721 return AccessElement(t_out_float, per_vertex, clip_distances_index,
722 abuf->GetElement() + 4);
723 default:
724 if (IsGenericAttribute(attribute)) {
725 return AccessElement(t_out_float, output_attributes.at(attribute),
726 abuf->GetElement());
727 }
728 UNIMPLEMENTED_MSG("Unhandled output attribute: {}",
729 static_cast<u32>(attribute));
730 return {};
731 }
732 }();
733
734 } else if (const auto lmem = std::get_if<LmemNode>(dest)) {
735 Id address = BitcastTo<Type::Uint>(Visit(lmem->GetAddress()));
736 address = Emit(OpUDiv(t_uint, address, Constant(t_uint, 4)));
737 target = Emit(OpAccessChain(t_prv_float, local_memory, {address}));
738 }
739
740 Emit(OpStore(target, Visit(src)));
741 return {};
742 }
743
744 Id HNegate(Operation operation) {
745 UNIMPLEMENTED();
746 return {};
747 }
748
749 Id HMergeF32(Operation operation) {
750 UNIMPLEMENTED();
751 return {};
752 }
753
754 Id HMergeH0(Operation operation) {
755 UNIMPLEMENTED();
756 return {};
757 }
758
759 Id HMergeH1(Operation operation) {
760 UNIMPLEMENTED();
761 return {};
762 }
763
764 Id HPack2(Operation operation) {
765 UNIMPLEMENTED();
766 return {};
767 }
768
769 Id LogicalAssign(Operation operation) {
770 const Node dest = operation[0];
771 const Node src = operation[1];
772
773 Id target{};
774 if (const auto pred = std::get_if<PredicateNode>(dest)) {
775 ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment");
776
777 const auto index = pred->GetIndex();
778 switch (index) {
779 case Tegra::Shader::Pred::NeverExecute:
780 case Tegra::Shader::Pred::UnusedIndex:
781 // Writing to these predicates is a no-op
782 return {};
783 }
784 target = predicates.at(index);
785
786 } else if (const auto flag = std::get_if<InternalFlagNode>(dest)) {
787 target = internal_flags.at(static_cast<u32>(flag->GetFlag()));
788 }
789
790 Emit(OpStore(target, Visit(src)));
791 return {};
792 }
793
794 Id LogicalPick2(Operation operation) {
795 UNIMPLEMENTED();
796 return {};
797 }
798
799 Id LogicalAll2(Operation operation) {
800 UNIMPLEMENTED();
801 return {};
802 }
803
804 Id LogicalAny2(Operation operation) {
805 UNIMPLEMENTED();
806 return {};
807 }
808
809 Id GetTextureSampler(Operation operation) {
810 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
811 const auto entry = sampler_images.at(static_cast<u32>(meta->sampler.GetIndex()));
812 return Emit(OpLoad(entry.sampled_image_type, entry.sampler));
813 }
814
815 Id GetTextureImage(Operation operation) {
816 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
817 const auto entry = sampler_images.at(static_cast<u32>(meta->sampler.GetIndex()));
818 return Emit(OpImage(entry.image_type, GetTextureSampler(operation)));
819 }
820
821 Id GetTextureCoordinates(Operation operation) {
822 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
823 std::vector<Id> coords;
824 for (std::size_t i = 0; i < operation.GetOperandsCount(); ++i) {
825 coords.push_back(Visit(operation[i]));
826 }
827 if (meta->sampler.IsArray()) {
828 const Id array_integer = BitcastTo<Type::Int>(Visit(meta->array));
829 coords.push_back(Emit(OpConvertSToF(t_float, array_integer)));
830 }
831 if (meta->sampler.IsShadow()) {
832 coords.push_back(Visit(meta->depth_compare));
833 }
834
835 const std::array<Id, 4> t_float_lut = {nullptr, t_float2, t_float3, t_float4};
836 return coords.size() == 1
837 ? coords[0]
838 : Emit(OpCompositeConstruct(t_float_lut.at(coords.size() - 1), coords));
839 }
840
841 Id GetTextureElement(Operation operation, Id sample_value) {
842 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
843 ASSERT(meta);
844 return Emit(OpCompositeExtract(t_float, sample_value, meta->element));
845 }
846
847 Id Texture(Operation operation) {
848 const Id texture = Emit(OpImageSampleImplicitLod(t_float4, GetTextureSampler(operation),
849 GetTextureCoordinates(operation)));
850 return GetTextureElement(operation, texture);
851 }
852
853 Id TextureLod(Operation operation) {
854 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
855 const Id texture = Emit(OpImageSampleExplicitLod(
856 t_float4, GetTextureSampler(operation), GetTextureCoordinates(operation),
857 spv::ImageOperandsMask::Lod, Visit(meta->lod)));
858 return GetTextureElement(operation, texture);
859 }
860
861 Id TextureGather(Operation operation) {
862 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
863 const auto coords = GetTextureCoordinates(operation);
864
865 Id texture;
866 if (meta->sampler.IsShadow()) {
867 texture = Emit(OpImageDrefGather(t_float4, GetTextureSampler(operation), coords,
868 Visit(meta->component)));
869 } else {
870 u32 component_value = 0;
871 if (meta->component) {
872 const auto component = std::get_if<ImmediateNode>(meta->component);
873 ASSERT_MSG(component, "Component is not an immediate value");
874 component_value = component->GetValue();
875 }
876 texture = Emit(OpImageGather(t_float4, GetTextureSampler(operation), coords,
877 Constant(t_uint, component_value)));
878 }
879
880 return GetTextureElement(operation, texture);
881 }
882
883 Id TextureQueryDimensions(Operation operation) {
884 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
885 const auto image_id = GetTextureImage(operation);
886 AddCapability(spv::Capability::ImageQuery);
887
888 if (meta->element == 3) {
889 return BitcastTo<Type::Float>(Emit(OpImageQueryLevels(t_int, image_id)));
890 }
891
892 const Id lod = VisitOperand<Type::Uint>(operation, 0);
893 const std::size_t coords_count = [&]() {
894 switch (const auto type = meta->sampler.GetType(); type) {
895 case Tegra::Shader::TextureType::Texture1D:
896 return 1;
897 case Tegra::Shader::TextureType::Texture2D:
898 case Tegra::Shader::TextureType::TextureCube:
899 return 2;
900 case Tegra::Shader::TextureType::Texture3D:
901 return 3;
902 default:
903 UNREACHABLE_MSG("Invalid texture type={}", static_cast<u32>(type));
904 return 2;
905 }
906 }();
907
908 if (meta->element >= coords_count) {
909 return Constant(t_float, 0.0f);
910 }
911
912 const std::array<Id, 3> types = {t_int, t_int2, t_int3};
913 const Id sizes = Emit(OpImageQuerySizeLod(types.at(coords_count - 1), image_id, lod));
914 const Id size = Emit(OpCompositeExtract(t_int, sizes, meta->element));
915 return BitcastTo<Type::Float>(size);
916 }
917
918 Id TextureQueryLod(Operation operation) {
919 UNIMPLEMENTED();
920 return {};
921 }
922
923 Id TexelFetch(Operation operation) {
924 UNIMPLEMENTED();
925 return {};
926 }
927
928 Id Branch(Operation operation) {
929 const auto target = std::get_if<ImmediateNode>(operation[0]);
930 UNIMPLEMENTED_IF(!target);
931
932 Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue())));
933 BranchingOp([&]() { Emit(OpBranch(continue_label)); });
934 return {};
935 }
936
937 Id PushFlowStack(Operation operation) {
938 const auto target = std::get_if<ImmediateNode>(operation[0]);
939 ASSERT(target);
940
941 const Id current = Emit(OpLoad(t_uint, flow_stack_top));
942 const Id next = Emit(OpIAdd(t_uint, current, Constant(t_uint, 1)));
943 const Id access = Emit(OpAccessChain(t_func_uint, flow_stack, current));
944
945 Emit(OpStore(access, Constant(t_uint, target->GetValue())));
946 Emit(OpStore(flow_stack_top, next));
947 return {};
948 }
949
950 Id PopFlowStack(Operation operation) {
951 const Id current = Emit(OpLoad(t_uint, flow_stack_top));
952 const Id previous = Emit(OpISub(t_uint, current, Constant(t_uint, 1)));
953 const Id access = Emit(OpAccessChain(t_func_uint, flow_stack, previous));
954 const Id target = Emit(OpLoad(t_uint, access));
955
956 Emit(OpStore(flow_stack_top, previous));
957 Emit(OpStore(jmp_to, target));
958 BranchingOp([&]() { Emit(OpBranch(continue_label)); });
959 return {};
960 }
961
962 Id Exit(Operation operation) {
963 switch (stage) {
964 case ShaderStage::Vertex: {
965 // TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't
966 // seem to be working on Nvidia's drivers and Intel (mesa and blob) doesn't support it.
967 const Id position = AccessElement(t_float4, per_vertex, position_index);
968 Id depth = Emit(OpLoad(t_float, AccessElement(t_out_float, position, 2)));
969 depth = Emit(OpFAdd(t_float, depth, Constant(t_float, 1.0f)));
970 depth = Emit(OpFMul(t_float, depth, Constant(t_float, 0.5f)));
971 Emit(OpStore(AccessElement(t_out_float, position, 2), depth));
972 break;
973 }
974 case ShaderStage::Fragment: {
975 const auto SafeGetRegister = [&](u32 reg) {
976 // TODO(Rodrigo): Replace with contains once C++20 releases
977 if (const auto it = registers.find(reg); it != registers.end()) {
978 return Emit(OpLoad(t_float, it->second));
979 }
980 return Constant(t_float, 0.0f);
981 };
982
983 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0,
984 "Sample mask write is unimplemented");
985
986 // TODO(Rodrigo): Alpha testing
987
988 // Write the color outputs using the data in the shader registers, disabled
989 // rendertargets/components are skipped in the register assignment.
990 u32 current_reg = 0;
991 for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) {
992 // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
993 for (u32 component = 0; component < 4; ++component) {
994 if (header.ps.IsColorComponentOutputEnabled(rt, component)) {
995 Emit(OpStore(AccessElement(t_out_float, frag_colors.at(rt), component),
996 SafeGetRegister(current_reg)));
997 ++current_reg;
998 }
999 }
1000 }
1001 if (header.ps.omap.depth) {
1002 // The depth output is always 2 registers after the last color output, and
1003 // current_reg already contains one past the last color register.
1004 Emit(OpStore(frag_depth, SafeGetRegister(current_reg + 1)));
1005 }
1006 break;
1007 }
1008 }
1009
1010 BranchingOp([&]() { Emit(OpReturn()); });
1011 return {};
1012 }
1013
1014 Id Discard(Operation operation) {
1015 BranchingOp([&]() { Emit(OpKill()); });
1016 return {};
1017 }
1018
1019 Id EmitVertex(Operation operation) {
1020 UNIMPLEMENTED();
1021 return {};
1022 }
1023
1024 Id EndPrimitive(Operation operation) {
1025 UNIMPLEMENTED();
1026 return {};
1027 }
1028
1029 Id YNegate(Operation operation) {
1030 UNIMPLEMENTED();
1031 return {};
1032 }
1033
1034 Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type,
1035 const std::string& name) {
1036 const Id id = OpVariable(type, storage);
1037 Decorate(id, spv::Decoration::BuiltIn, static_cast<u32>(builtin));
1038 AddGlobalVariable(Name(id, name));
1039 interfaces.push_back(id);
1040 return id;
1041 }
1042
1043 bool IsRenderTargetUsed(u32 rt) const {
1044 for (u32 component = 0; component < 4; ++component) {
1045 if (header.ps.IsColorComponentOutputEnabled(rt, component)) {
1046 return true;
1047 }
1048 }
1049 return false;
1050 }
1051
1052 template <typename... Args>
1053 Id AccessElement(Id pointer_type, Id composite, Args... elements_) {
1054 std::vector<Id> members;
1055 auto elements = {elements_...};
1056 for (const auto element : elements) {
1057 members.push_back(Constant(t_uint, element));
1058 }
1059
1060 return Emit(OpAccessChain(pointer_type, composite, members));
1061 }
1062
1063 template <Type type>
1064 Id VisitOperand(Operation operation, std::size_t operand_index) {
1065 const Id value = Visit(operation[operand_index]);
1066
1067 switch (type) {
1068 case Type::Bool:
1069 case Type::Bool2:
1070 case Type::Float:
1071 return value;
1072 case Type::Int:
1073 return Emit(OpBitcast(t_int, value));
1074 case Type::Uint:
1075 return Emit(OpBitcast(t_uint, value));
1076 case Type::HalfFloat:
1077 UNIMPLEMENTED();
1078 }
1079 UNREACHABLE();
1080 return value;
1081 }
1082
1083 template <Type type>
1084 Id BitcastFrom(Id value) {
1085 switch (type) {
1086 case Type::Bool:
1087 case Type::Bool2:
1088 case Type::Float:
1089 return value;
1090 case Type::Int:
1091 case Type::Uint:
1092 return Emit(OpBitcast(t_float, value));
1093 case Type::HalfFloat:
1094 UNIMPLEMENTED();
1095 }
1096 UNREACHABLE();
1097 return value;
1098 }
1099
1100 template <Type type>
1101 Id BitcastTo(Id value) {
1102 switch (type) {
1103 case Type::Bool:
1104 case Type::Bool2:
1105 UNREACHABLE();
1106 case Type::Float:
1107 return Emit(OpBitcast(t_float, value));
1108 case Type::Int:
1109 return Emit(OpBitcast(t_int, value));
1110 case Type::Uint:
1111 return Emit(OpBitcast(t_uint, value));
1112 case Type::HalfFloat:
1113 UNIMPLEMENTED();
1114 }
1115 UNREACHABLE();
1116 return value;
1117 }
1118
1119 Id GetTypeDefinition(Type type) {
1120 switch (type) {
1121 case Type::Bool:
1122 return t_bool;
1123 case Type::Bool2:
1124 return t_bool2;
1125 case Type::Float:
1126 return t_float;
1127 case Type::Int:
1128 return t_int;
1129 case Type::Uint:
1130 return t_uint;
1131 case Type::HalfFloat:
1132 UNIMPLEMENTED();
1133 }
1134 UNREACHABLE();
1135 return {};
1136 }
1137
1138 void BranchingOp(std::function<void()> call) {
1139 const Id true_label = OpLabel();
1140 const Id skip_label = OpLabel();
1141 Emit(OpSelectionMerge(skip_label, spv::SelectionControlMask::Flatten));
1142 Emit(OpBranchConditional(v_true, true_label, skip_label, 1, 0));
1143 Emit(true_label);
1144 call();
1145
1146 Emit(skip_label);
1147 }
1148
1149 static constexpr OperationDecompilersArray operation_decompilers = {
1150 &SPIRVDecompiler::Assign,
1151
1152 &SPIRVDecompiler::Ternary<&Module::OpSelect, Type::Float, Type::Bool, Type::Float,
1153 Type::Float>,
1154
1155 &SPIRVDecompiler::Binary<&Module::OpFAdd, Type::Float>,
1156 &SPIRVDecompiler::Binary<&Module::OpFMul, Type::Float>,
1157 &SPIRVDecompiler::Binary<&Module::OpFDiv, Type::Float>,
1158 &SPIRVDecompiler::Ternary<&Module::OpFma, Type::Float>,
1159 &SPIRVDecompiler::Unary<&Module::OpFNegate, Type::Float>,
1160 &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::Float>,
1161 &SPIRVDecompiler::Ternary<&Module::OpFClamp, Type::Float>,
1162 &SPIRVDecompiler::Binary<&Module::OpFMin, Type::Float>,
1163 &SPIRVDecompiler::Binary<&Module::OpFMax, Type::Float>,
1164 &SPIRVDecompiler::Unary<&Module::OpCos, Type::Float>,
1165 &SPIRVDecompiler::Unary<&Module::OpSin, Type::Float>,
1166 &SPIRVDecompiler::Unary<&Module::OpExp2, Type::Float>,
1167 &SPIRVDecompiler::Unary<&Module::OpLog2, Type::Float>,
1168 &SPIRVDecompiler::Unary<&Module::OpInverseSqrt, Type::Float>,
1169 &SPIRVDecompiler::Unary<&Module::OpSqrt, Type::Float>,
1170 &SPIRVDecompiler::Unary<&Module::OpRoundEven, Type::Float>,
1171 &SPIRVDecompiler::Unary<&Module::OpFloor, Type::Float>,
1172 &SPIRVDecompiler::Unary<&Module::OpCeil, Type::Float>,
1173 &SPIRVDecompiler::Unary<&Module::OpTrunc, Type::Float>,
1174 &SPIRVDecompiler::Unary<&Module::OpConvertSToF, Type::Float, Type::Int>,
1175 &SPIRVDecompiler::Unary<&Module::OpConvertUToF, Type::Float, Type::Uint>,
1176
1177 &SPIRVDecompiler::Binary<&Module::OpIAdd, Type::Int>,
1178 &SPIRVDecompiler::Binary<&Module::OpIMul, Type::Int>,
1179 &SPIRVDecompiler::Binary<&Module::OpSDiv, Type::Int>,
1180 &SPIRVDecompiler::Unary<&Module::OpSNegate, Type::Int>,
1181 &SPIRVDecompiler::Unary<&Module::OpSAbs, Type::Int>,
1182 &SPIRVDecompiler::Binary<&Module::OpSMin, Type::Int>,
1183 &SPIRVDecompiler::Binary<&Module::OpSMax, Type::Int>,
1184
1185 &SPIRVDecompiler::Unary<&Module::OpConvertFToS, Type::Int, Type::Float>,
1186 &SPIRVDecompiler::Unary<&Module::OpBitcast, Type::Int, Type::Uint>,
1187 &SPIRVDecompiler::Binary<&Module::OpShiftLeftLogical, Type::Int, Type::Int, Type::Uint>,
1188 &SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Int, Type::Int, Type::Uint>,
1189 &SPIRVDecompiler::Binary<&Module::OpShiftRightArithmetic, Type::Int, Type::Int, Type::Uint>,
1190 &SPIRVDecompiler::Binary<&Module::OpBitwiseAnd, Type::Int>,
1191 &SPIRVDecompiler::Binary<&Module::OpBitwiseOr, Type::Int>,
1192 &SPIRVDecompiler::Binary<&Module::OpBitwiseXor, Type::Int>,
1193 &SPIRVDecompiler::Unary<&Module::OpNot, Type::Int>,
1194 &SPIRVDecompiler::Quaternary<&Module::OpBitFieldInsert, Type::Int>,
1195 &SPIRVDecompiler::Ternary<&Module::OpBitFieldSExtract, Type::Int>,
1196 &SPIRVDecompiler::Unary<&Module::OpBitCount, Type::Int>,
1197
1198 &SPIRVDecompiler::Binary<&Module::OpIAdd, Type::Uint>,
1199 &SPIRVDecompiler::Binary<&Module::OpIMul, Type::Uint>,
1200 &SPIRVDecompiler::Binary<&Module::OpUDiv, Type::Uint>,
1201 &SPIRVDecompiler::Binary<&Module::OpUMin, Type::Uint>,
1202 &SPIRVDecompiler::Binary<&Module::OpUMax, Type::Uint>,
1203 &SPIRVDecompiler::Unary<&Module::OpConvertFToU, Type::Uint, Type::Float>,
1204 &SPIRVDecompiler::Unary<&Module::OpBitcast, Type::Uint, Type::Int>,
1205 &SPIRVDecompiler::Binary<&Module::OpShiftLeftLogical, Type::Uint>,
1206 &SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Uint>,
1207 &SPIRVDecompiler::Binary<&Module::OpShiftRightArithmetic, Type::Uint>,
1208 &SPIRVDecompiler::Binary<&Module::OpBitwiseAnd, Type::Uint>,
1209 &SPIRVDecompiler::Binary<&Module::OpBitwiseOr, Type::Uint>,
1210 &SPIRVDecompiler::Binary<&Module::OpBitwiseXor, Type::Uint>,
1211 &SPIRVDecompiler::Unary<&Module::OpNot, Type::Uint>,
1212 &SPIRVDecompiler::Quaternary<&Module::OpBitFieldInsert, Type::Uint>,
1213 &SPIRVDecompiler::Ternary<&Module::OpBitFieldUExtract, Type::Uint>,
1214 &SPIRVDecompiler::Unary<&Module::OpBitCount, Type::Uint>,
1215
1216 &SPIRVDecompiler::Binary<&Module::OpFAdd, Type::HalfFloat>,
1217 &SPIRVDecompiler::Binary<&Module::OpFMul, Type::HalfFloat>,
1218 &SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>,
1219 &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>,
1220 &SPIRVDecompiler::HNegate,
1221 &SPIRVDecompiler::HMergeF32,
1222 &SPIRVDecompiler::HMergeH0,
1223 &SPIRVDecompiler::HMergeH1,
1224 &SPIRVDecompiler::HPack2,
1225
1226 &SPIRVDecompiler::LogicalAssign,
1227 &SPIRVDecompiler::Binary<&Module::OpLogicalAnd, Type::Bool>,
1228 &SPIRVDecompiler::Binary<&Module::OpLogicalOr, Type::Bool>,
1229 &SPIRVDecompiler::Binary<&Module::OpLogicalNotEqual, Type::Bool>,
1230 &SPIRVDecompiler::Unary<&Module::OpLogicalNot, Type::Bool>,
1231 &SPIRVDecompiler::LogicalPick2,
1232 &SPIRVDecompiler::LogicalAll2,
1233 &SPIRVDecompiler::LogicalAny2,
1234
1235 &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::Float>,
1236 &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::Float>,
1237 &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::Float>,
1238 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::Float>,
1239 &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::Float>,
1240 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::Float>,
1241 &SPIRVDecompiler::Unary<&Module::OpIsNan, Type::Bool>,
1242
1243 &SPIRVDecompiler::Binary<&Module::OpSLessThan, Type::Bool, Type::Int>,
1244 &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Int>,
1245 &SPIRVDecompiler::Binary<&Module::OpSLessThanEqual, Type::Bool, Type::Int>,
1246 &SPIRVDecompiler::Binary<&Module::OpSGreaterThan, Type::Bool, Type::Int>,
1247 &SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Int>,
1248 &SPIRVDecompiler::Binary<&Module::OpSGreaterThanEqual, Type::Bool, Type::Int>,
1249
1250 &SPIRVDecompiler::Binary<&Module::OpULessThan, Type::Bool, Type::Uint>,
1251 &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Uint>,
1252 &SPIRVDecompiler::Binary<&Module::OpULessThanEqual, Type::Bool, Type::Uint>,
1253 &SPIRVDecompiler::Binary<&Module::OpUGreaterThan, Type::Bool, Type::Uint>,
1254 &SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Uint>,
1255 &SPIRVDecompiler::Binary<&Module::OpUGreaterThanEqual, Type::Bool, Type::Uint>,
1256
1257 &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::HalfFloat>,
1258 &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::HalfFloat>,
1259 &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::HalfFloat>,
1260 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>,
1261 &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>,
1262 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>,
1263
1264 &SPIRVDecompiler::Texture,
1265 &SPIRVDecompiler::TextureLod,
1266 &SPIRVDecompiler::TextureGather,
1267 &SPIRVDecompiler::TextureQueryDimensions,
1268 &SPIRVDecompiler::TextureQueryLod,
1269 &SPIRVDecompiler::TexelFetch,
1270
1271 &SPIRVDecompiler::Branch,
1272 &SPIRVDecompiler::PushFlowStack,
1273 &SPIRVDecompiler::PopFlowStack,
1274 &SPIRVDecompiler::Exit,
1275 &SPIRVDecompiler::Discard,
1276
1277 &SPIRVDecompiler::EmitVertex,
1278 &SPIRVDecompiler::EndPrimitive,
1279
1280 &SPIRVDecompiler::YNegate,
1281 };
1282
1283 const ShaderIR& ir;
1284 const ShaderStage stage;
1285 const Tegra::Shader::Header header;
1286
1287 const Id t_void = Name(TypeVoid(), "void");
1288
1289 const Id t_bool = Name(TypeBool(), "bool");
1290 const Id t_bool2 = Name(TypeVector(t_bool, 2), "bool2");
1291
1292 const Id t_int = Name(TypeInt(32, true), "int");
1293 const Id t_int2 = Name(TypeVector(t_int, 2), "int2");
1294 const Id t_int3 = Name(TypeVector(t_int, 3), "int3");
1295 const Id t_int4 = Name(TypeVector(t_int, 4), "int4");
1296
1297 const Id t_uint = Name(TypeInt(32, false), "uint");
1298 const Id t_uint2 = Name(TypeVector(t_uint, 2), "uint2");
1299 const Id t_uint3 = Name(TypeVector(t_uint, 3), "uint3");
1300 const Id t_uint4 = Name(TypeVector(t_uint, 4), "uint4");
1301
1302 const Id t_float = Name(TypeFloat(32), "float");
1303 const Id t_float2 = Name(TypeVector(t_float, 2), "float2");
1304 const Id t_float3 = Name(TypeVector(t_float, 3), "float3");
1305 const Id t_float4 = Name(TypeVector(t_float, 4), "float4");
1306
1307 const Id t_prv_bool = Name(TypePointer(spv::StorageClass::Private, t_bool), "prv_bool");
1308 const Id t_prv_float = Name(TypePointer(spv::StorageClass::Private, t_float), "prv_float");
1309
1310 const Id t_func_uint = Name(TypePointer(spv::StorageClass::Function, t_uint), "func_uint");
1311
1312 const Id t_in_bool = Name(TypePointer(spv::StorageClass::Input, t_bool), "in_bool");
1313 const Id t_in_uint = Name(TypePointer(spv::StorageClass::Input, t_uint), "in_uint");
1314 const Id t_in_float = Name(TypePointer(spv::StorageClass::Input, t_float), "in_float");
1315 const Id t_in_float4 = Name(TypePointer(spv::StorageClass::Input, t_float4), "in_float4");
1316
1317 const Id t_out_float = Name(TypePointer(spv::StorageClass::Output, t_float), "out_float");
1318 const Id t_out_float4 = Name(TypePointer(spv::StorageClass::Output, t_float4), "out_float4");
1319
1320 const Id t_cbuf_float = TypePointer(spv::StorageClass::Uniform, t_float);
1321 const Id t_cbuf_array =
1322 Decorate(Name(TypeArray(t_float4, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS)), "CbufArray"),
1323 spv::Decoration::ArrayStride, CBUF_STRIDE);
1324 const Id t_cbuf_struct = MemberDecorate(
1325 Decorate(TypeStruct(t_cbuf_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
1326 const Id t_cbuf_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_struct);
1327
1328 const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float);
1329 const Id t_gmem_array =
1330 Name(Decorate(TypeRuntimeArray(t_float), spv::Decoration::ArrayStride, 4u), "GmemArray");
1331 const Id t_gmem_struct = MemberDecorate(
1332 Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
1333 const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct);
1334
1335 const Id v_float_zero = Constant(t_float, 0.0f);
1336 const Id v_true = ConstantTrue(t_bool);
1337 const Id v_false = ConstantFalse(t_bool);
1338
1339 Id per_vertex{};
1340 std::map<u32, Id> registers;
1341 std::map<Tegra::Shader::Pred, Id> predicates;
1342 Id local_memory{};
1343 std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{};
1344 std::map<Attribute::Index, Id> input_attributes;
1345 std::map<Attribute::Index, Id> output_attributes;
1346 std::map<u32, Id> constant_buffers;
1347 std::map<GlobalMemoryBase, Id> global_buffers;
1348 std::map<u32, SamplerImage> sampler_images;
1349
1350 Id instance_index{};
1351 Id vertex_index{};
1352 std::array<Id, Maxwell::NumRenderTargets> frag_colors{};
1353 Id frag_depth{};
1354 Id frag_coord{};
1355 Id front_facing{};
1356
1357 u32 position_index{};
1358 u32 point_size_index{};
1359 u32 clip_distances_index{};
1360
1361 std::vector<Id> interfaces;
1362
1363 u32 const_buffers_base_binding{};
1364 u32 global_buffers_base_binding{};
1365 u32 samplers_base_binding{};
1366
1367 Id execute_function{};
1368 Id jmp_to{};
1369 Id flow_stack_top{};
1370 Id flow_stack{};
1371 Id continue_label{};
1372 std::map<u32, Id> labels;
1373};
1374
1375DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage) {
1376 auto decompiler = std::make_unique<SPIRVDecompiler>(ir, stage);
1377 decompiler->Decompile();
1378 return {std::move(decompiler), decompiler->GetShaderEntries()};
1379}
1380
1381} // namespace Vulkan::VKShader
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
new file mode 100644
index 000000000..329d8fa38
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
@@ -0,0 +1,80 @@
1// Copyright 2019 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 <array>
8#include <memory>
9#include <set>
10#include <utility>
11#include <vector>
12
13#include <sirit/sirit.h>
14
15#include "common/common_types.h"
16#include "video_core/engines/maxwell_3d.h"
17#include "video_core/shader/shader_ir.h"
18
19namespace VideoCommon::Shader {
20class ShaderIR;
21}
22
23namespace Vulkan::VKShader {
24
25using Maxwell = Tegra::Engines::Maxwell3D::Regs;
26
27using SamplerEntry = VideoCommon::Shader::Sampler;
28
29constexpr u32 DESCRIPTOR_SET = 0;
30
31class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer {
32public:
33 explicit constexpr ConstBufferEntry(const VideoCommon::Shader::ConstBuffer& entry, u32 index)
34 : VideoCommon::Shader::ConstBuffer{entry}, index{index} {}
35
36 constexpr u32 GetIndex() const {
37 return index;
38 }
39
40private:
41 u32 index{};
42};
43
44class GlobalBufferEntry {
45public:
46 explicit GlobalBufferEntry(u32 cbuf_index, u32 cbuf_offset)
47 : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {}
48
49 u32 GetCbufIndex() const {
50 return cbuf_index;
51 }
52
53 u32 GetCbufOffset() const {
54 return cbuf_offset;
55 }
56
57private:
58 u32 cbuf_index{};
59 u32 cbuf_offset{};
60};
61
62struct ShaderEntries {
63 u32 const_buffers_base_binding{};
64 u32 global_buffers_base_binding{};
65 u32 samplers_base_binding{};
66 std::vector<ConstBufferEntry> const_buffers;
67 std::vector<GlobalBufferEntry> global_buffers;
68 std::vector<SamplerEntry> samplers;
69 std::set<u32> attributes;
70 std::array<bool, Maxwell::NumClipDistances> clip_distances{};
71 std::size_t shader_length{};
72 Sirit::Id entry_function{};
73 std::vector<Sirit::Id> interfaces;
74};
75
76using DecompilerResult = std::pair<std::unique_ptr<Sirit::Module>, ShaderEntries>;
77
78DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage);
79
80} // namespace Vulkan::VKShader
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index b2b706cb8..ea1092db1 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -19,6 +19,23 @@ using Tegra::Shader::Instruction;
19using Tegra::Shader::OpCode; 19using Tegra::Shader::OpCode;
20using Tegra::Shader::Register; 20using Tegra::Shader::Register;
21 21
22namespace {
23u32 GetUniformTypeElementsCount(Tegra::Shader::UniformType uniform_type) {
24 switch (uniform_type) {
25 case Tegra::Shader::UniformType::Single:
26 return 1;
27 case Tegra::Shader::UniformType::Double:
28 return 2;
29 case Tegra::Shader::UniformType::Quad:
30 case Tegra::Shader::UniformType::UnsignedQuad:
31 return 4;
32 default:
33 UNIMPLEMENTED_MSG("Unimplemented size={}!", static_cast<u32>(uniform_type));
34 return 1;
35 }
36}
37} // namespace
38
22u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { 39u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
23 const Instruction instr = {program_code[pc]}; 40 const Instruction instr = {program_code[pc]};
24 const auto opcode = OpCode::Decode(instr); 41 const auto opcode = OpCode::Decode(instr);
@@ -127,45 +144,15 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
127 break; 144 break;
128 } 145 }
129 case OpCode::Id::LDG: { 146 case OpCode::Id::LDG: {
130 const u32 count = [&]() { 147 const auto [real_address_base, base_address, descriptor] =
131 switch (instr.ldg.type) { 148 TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8),
132 case Tegra::Shader::UniformType::Single: 149 static_cast<u32>(instr.ldg.immediate_offset.Value()), false);
133 return 1;
134 case Tegra::Shader::UniformType::Double:
135 return 2;
136 case Tegra::Shader::UniformType::Quad:
137 case Tegra::Shader::UniformType::UnsignedQuad:
138 return 4;
139 default:
140 UNIMPLEMENTED_MSG("Unimplemented LDG size!");
141 return 1;
142 }
143 }();
144
145 const Node addr_register = GetRegister(instr.gpr8);
146 const Node base_address =
147 TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()));
148 const auto cbuf = std::get_if<CbufNode>(base_address);
149 ASSERT(cbuf != nullptr);
150 const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
151 ASSERT(cbuf_offset_imm != nullptr);
152 const auto cbuf_offset = cbuf_offset_imm->GetValue();
153
154 bb.push_back(Comment(
155 fmt::format("Base address is c[0x{:x}][0x{:x}]", cbuf->GetIndex(), cbuf_offset)));
156
157 const GlobalMemoryBase descriptor{cbuf->GetIndex(), cbuf_offset};
158 used_global_memory_bases.insert(descriptor);
159
160 const Node immediate_offset =
161 Immediate(static_cast<u32>(instr.ldg.immediate_offset.Value()));
162 const Node base_real_address =
163 Operation(OperationCode::UAdd, NO_PRECISE, immediate_offset, addr_register);
164 150
151 const u32 count = GetUniformTypeElementsCount(instr.ldg.type);
165 for (u32 i = 0; i < count; ++i) { 152 for (u32 i = 0; i < count; ++i) {
166 const Node it_offset = Immediate(i * 4); 153 const Node it_offset = Immediate(i * 4);
167 const Node real_address = 154 const Node real_address =
168 Operation(OperationCode::UAdd, NO_PRECISE, base_real_address, it_offset); 155 Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
169 const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); 156 const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
170 157
171 SetTemporal(bb, i, gmem); 158 SetTemporal(bb, i, gmem);
@@ -175,6 +162,28 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
175 } 162 }
176 break; 163 break;
177 } 164 }
165 case OpCode::Id::STG: {
166 const auto [real_address_base, base_address, descriptor] =
167 TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8),
168 static_cast<u32>(instr.stg.immediate_offset.Value()), true);
169
170 // Encode in temporary registers like this: real_base_address, {registers_to_be_written...}
171 SetTemporal(bb, 0, real_address_base);
172
173 const u32 count = GetUniformTypeElementsCount(instr.stg.type);
174 for (u32 i = 0; i < count; ++i) {
175 SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i));
176 }
177 for (u32 i = 0; i < count; ++i) {
178 const Node it_offset = Immediate(i * 4);
179 const Node real_address =
180 Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
181 const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
182
183 bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1)));
184 }
185 break;
186 }
178 case OpCode::Id::ST_A: { 187 case OpCode::Id::ST_A: {
179 UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, 188 UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
180 "Indirect attribute loads are not supported"); 189 "Indirect attribute loads are not supported");
@@ -237,4 +246,34 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
237 return pc; 246 return pc;
238} 247}
239 248
249std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeBlock& bb,
250 Node addr_register,
251 u32 immediate_offset,
252 bool is_write) {
253 const Node base_address{
254 TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))};
255 const auto cbuf = std::get_if<CbufNode>(base_address);
256 ASSERT(cbuf != nullptr);
257 const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
258 ASSERT(cbuf_offset_imm != nullptr);
259 const auto cbuf_offset = cbuf_offset_imm->GetValue();
260
261 bb.push_back(
262 Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", cbuf->GetIndex(), cbuf_offset)));
263
264 const GlobalMemoryBase descriptor{cbuf->GetIndex(), cbuf_offset};
265 const auto& [entry, is_new] = used_global_memory.try_emplace(descriptor);
266 auto& usage = entry->second;
267 if (is_write) {
268 usage.is_written = true;
269 } else {
270 usage.is_read = true;
271 }
272
273 const auto real_address =
274 Operation(OperationCode::UAdd, NO_PRECISE, Immediate(immediate_offset), addr_register);
275
276 return {real_address, base_address, descriptor};
277}
278
240} // namespace VideoCommon::Shader 279} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index a99ae19bf..a775b402b 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -7,7 +7,9 @@
7#include <fmt/format.h> 7#include <fmt/format.h>
8 8
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/bit_field.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/logging/log.h"
11#include "video_core/engines/shader_bytecode.h" 13#include "video_core/engines/shader_bytecode.h"
12#include "video_core/shader/shader_ir.h" 14#include "video_core/shader/shader_ir.h"
13 15
@@ -41,19 +43,18 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
41 43
42 switch (opcode->get().GetId()) { 44 switch (opcode->get().GetId()) {
43 case OpCode::Id::TEX: { 45 case OpCode::Id::TEX: {
44 UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(TextureMiscMode::AOFFI),
45 "AOFFI is not implemented");
46
47 if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) { 46 if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) {
48 LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete"); 47 LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete");
49 } 48 }
50 49
51 const TextureType texture_type{instr.tex.texture_type}; 50 const TextureType texture_type{instr.tex.texture_type};
52 const bool is_array = instr.tex.array != 0; 51 const bool is_array = instr.tex.array != 0;
52 const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI);
53 const bool depth_compare = instr.tex.UsesMiscMode(TextureMiscMode::DC); 53 const bool depth_compare = instr.tex.UsesMiscMode(TextureMiscMode::DC);
54 const auto process_mode = instr.tex.GetTextureProcessMode(); 54 const auto process_mode = instr.tex.GetTextureProcessMode();
55 WriteTexInstructionFloat( 55 WriteTexInstructionFloat(
56 bb, instr, GetTexCode(instr, texture_type, process_mode, depth_compare, is_array)); 56 bb, instr,
57 GetTexCode(instr, texture_type, process_mode, depth_compare, is_array, is_aoffi));
57 break; 58 break;
58 } 59 }
59 case OpCode::Id::TEXS: { 60 case OpCode::Id::TEXS: {
@@ -78,8 +79,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
78 } 79 }
79 case OpCode::Id::TLD4: { 80 case OpCode::Id::TLD4: {
80 ASSERT(instr.tld4.array == 0); 81 ASSERT(instr.tld4.array == 0);
81 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::AOFFI),
82 "AOFFI is not implemented");
83 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::NDV), 82 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::NDV),
84 "NDV is not implemented"); 83 "NDV is not implemented");
85 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::PTP), 84 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::PTP),
@@ -92,8 +91,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
92 const auto texture_type = instr.tld4.texture_type.Value(); 91 const auto texture_type = instr.tld4.texture_type.Value();
93 const bool depth_compare = instr.tld4.UsesMiscMode(TextureMiscMode::DC); 92 const bool depth_compare = instr.tld4.UsesMiscMode(TextureMiscMode::DC);
94 const bool is_array = instr.tld4.array != 0; 93 const bool is_array = instr.tld4.array != 0;
95 WriteTexInstructionFloat(bb, instr, 94 const bool is_aoffi = instr.tld4.UsesMiscMode(TextureMiscMode::AOFFI);
96 GetTld4Code(instr, texture_type, depth_compare, is_array)); 95 WriteTexInstructionFloat(
96 bb, instr, GetTld4Code(instr, texture_type, depth_compare, is_array, is_aoffi));
97 break; 97 break;
98 } 98 }
99 case OpCode::Id::TLD4S: { 99 case OpCode::Id::TLD4S: {
@@ -127,7 +127,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
127 Node4 values; 127 Node4 values;
128 for (u32 element = 0; element < values.size(); ++element) { 128 for (u32 element = 0; element < values.size(); ++element) {
129 auto coords_copy = coords; 129 auto coords_copy = coords;
130 MetaTexture meta{sampler, {}, {}, {}, {}, component, element}; 130 MetaTexture meta{sampler, {}, {}, {}, {}, {}, component, element};
131 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); 131 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
132 } 132 }
133 133
@@ -152,7 +152,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
152 if (!instr.txq.IsComponentEnabled(element)) { 152 if (!instr.txq.IsComponentEnabled(element)) {
153 continue; 153 continue;
154 } 154 }
155 MetaTexture meta{sampler, {}, {}, {}, {}, {}, element}; 155 MetaTexture meta{sampler, {}, {}, {}, {}, {}, {}, element};
156 const Node value = 156 const Node value =
157 Operation(OperationCode::TextureQueryDimensions, meta, GetRegister(instr.gpr8)); 157 Operation(OperationCode::TextureQueryDimensions, meta, GetRegister(instr.gpr8));
158 SetTemporal(bb, indexer++, value); 158 SetTemporal(bb, indexer++, value);
@@ -202,7 +202,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
202 202
203 for (u32 element = 0; element < 2; ++element) { 203 for (u32 element = 0; element < 2; ++element) {
204 auto params = coords; 204 auto params = coords;
205 MetaTexture meta{sampler, {}, {}, {}, {}, {}, element}; 205 MetaTexture meta{sampler, {}, {}, {}, {}, {}, {}, element};
206 const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); 206 const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params));
207 SetTemporal(bb, element, value); 207 SetTemporal(bb, element, value);
208 } 208 }
@@ -325,7 +325,8 @@ void ShaderIR::WriteTexsInstructionHalfFloat(NodeBlock& bb, Instruction instr,
325 325
326Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, 326Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
327 TextureProcessMode process_mode, std::vector<Node> coords, 327 TextureProcessMode process_mode, std::vector<Node> coords,
328 Node array, Node depth_compare, u32 bias_offset) { 328 Node array, Node depth_compare, u32 bias_offset,
329 std::vector<Node> aoffi) {
329 const bool is_array = array; 330 const bool is_array = array;
330 const bool is_shadow = depth_compare; 331 const bool is_shadow = depth_compare;
331 332
@@ -374,7 +375,7 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
374 Node4 values; 375 Node4 values;
375 for (u32 element = 0; element < values.size(); ++element) { 376 for (u32 element = 0; element < values.size(); ++element) {
376 auto copy_coords = coords; 377 auto copy_coords = coords;
377 MetaTexture meta{sampler, array, depth_compare, bias, lod, {}, element}; 378 MetaTexture meta{sampler, array, depth_compare, aoffi, bias, lod, {}, element};
378 values[element] = Operation(read_method, meta, std::move(copy_coords)); 379 values[element] = Operation(read_method, meta, std::move(copy_coords));
379 } 380 }
380 381
@@ -382,9 +383,15 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
382} 383}
383 384
384Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type, 385Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
385 TextureProcessMode process_mode, bool depth_compare, bool is_array) { 386 TextureProcessMode process_mode, bool depth_compare, bool is_array,
386 const bool lod_bias_enabled = 387 bool is_aoffi) {
387 (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ); 388 const bool lod_bias_enabled{
389 (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ)};
390
391 u64 parameter_register = instr.gpr20.Value();
392 if (lod_bias_enabled) {
393 ++parameter_register;
394 }
388 395
389 const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement( 396 const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement(
390 texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5); 397 texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5);
@@ -404,15 +411,19 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
404 411
405 const Node array = is_array ? GetRegister(array_register) : nullptr; 412 const Node array = is_array ? GetRegister(array_register) : nullptr;
406 413
414 std::vector<Node> aoffi;
415 if (is_aoffi) {
416 aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false);
417 }
418
407 Node dc{}; 419 Node dc{};
408 if (depth_compare) { 420 if (depth_compare) {
409 // Depth is always stored in the register signaled by gpr20 or in the next register if lod 421 // Depth is always stored in the register signaled by gpr20 or in the next register if lod
410 // or bias are used 422 // or bias are used
411 const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0); 423 dc = GetRegister(parameter_register++);
412 dc = GetRegister(depth_register);
413 } 424 }
414 425
415 return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, 0); 426 return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, 0, aoffi);
416} 427}
417 428
418Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type, 429Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
@@ -448,11 +459,11 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
448 dc = GetRegister(depth_register); 459 dc = GetRegister(depth_register);
449 } 460 }
450 461
451 return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_offset); 462 return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_offset, {});
452} 463}
453 464
454Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare, 465Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare,
455 bool is_array) { 466 bool is_array, bool is_aoffi) {
456 const std::size_t coord_count = GetCoordCount(texture_type); 467 const std::size_t coord_count = GetCoordCount(texture_type);
457 const std::size_t total_coord_count = coord_count + (is_array ? 1 : 0); 468 const std::size_t total_coord_count = coord_count + (is_array ? 1 : 0);
458 const std::size_t total_reg_count = total_coord_count + (depth_compare ? 1 : 0); 469 const std::size_t total_reg_count = total_coord_count + (depth_compare ? 1 : 0);
@@ -463,15 +474,27 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
463 const u64 coord_register = array_register + (is_array ? 1 : 0); 474 const u64 coord_register = array_register + (is_array ? 1 : 0);
464 475
465 std::vector<Node> coords; 476 std::vector<Node> coords;
466 for (size_t i = 0; i < coord_count; ++i) 477 for (std::size_t i = 0; i < coord_count; ++i) {
467 coords.push_back(GetRegister(coord_register + i)); 478 coords.push_back(GetRegister(coord_register + i));
479 }
480
481 u64 parameter_register = instr.gpr20.Value();
482 std::vector<Node> aoffi;
483 if (is_aoffi) {
484 aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, true);
485 }
486
487 Node dc{};
488 if (depth_compare) {
489 dc = GetRegister(parameter_register++);
490 }
468 491
469 const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare); 492 const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare);
470 493
471 Node4 values; 494 Node4 values;
472 for (u32 element = 0; element < values.size(); ++element) { 495 for (u32 element = 0; element < values.size(); ++element) {
473 auto coords_copy = coords; 496 auto coords_copy = coords;
474 MetaTexture meta{sampler, GetRegister(array_register), {}, {}, {}, {}, element}; 497 MetaTexture meta{sampler, GetRegister(array_register), dc, aoffi, {}, {}, {}, element};
475 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); 498 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
476 } 499 }
477 500
@@ -507,7 +530,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
507 Node4 values; 530 Node4 values;
508 for (u32 element = 0; element < values.size(); ++element) { 531 for (u32 element = 0; element < values.size(); ++element) {
509 auto coords_copy = coords; 532 auto coords_copy = coords;
510 MetaTexture meta{sampler, array, {}, {}, lod, {}, element}; 533 MetaTexture meta{sampler, array, {}, {}, {}, lod, {}, element};
511 values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); 534 values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
512 } 535 }
513 return values; 536 return values;
@@ -531,4 +554,45 @@ std::tuple<std::size_t, std::size_t> ShaderIR::ValidateAndGetCoordinateElement(
531 return {coord_count, total_coord_count}; 554 return {coord_count, total_coord_count};
532} 555}
533 556
534} // namespace VideoCommon::Shader \ No newline at end of file 557std::vector<Node> ShaderIR::GetAoffiCoordinates(Node aoffi_reg, std::size_t coord_count,
558 bool is_tld4) {
559 const auto [coord_offsets, size, wrap_value,
560 diff_value] = [is_tld4]() -> std::tuple<std::array<u32, 3>, u32, s32, s32> {
561 if (is_tld4) {
562 return {{0, 8, 16}, 6, 32, 64};
563 } else {
564 return {{0, 4, 8}, 4, 8, 16};
565 }
566 }();
567 const u32 mask = (1U << size) - 1;
568
569 std::vector<Node> aoffi;
570 aoffi.reserve(coord_count);
571
572 const auto aoffi_immediate{
573 TrackImmediate(aoffi_reg, global_code, static_cast<s64>(global_code.size()))};
574 if (!aoffi_immediate) {
575 // Variable access, not supported on AMD.
576 LOG_WARNING(HW_GPU,
577 "AOFFI constant folding failed, some hardware might have graphical issues");
578 for (std::size_t coord = 0; coord < coord_count; ++coord) {
579 const Node value = BitfieldExtract(aoffi_reg, coord_offsets.at(coord), size);
580 const Node condition =
581 Operation(OperationCode::LogicalIGreaterEqual, value, Immediate(wrap_value));
582 const Node negative = Operation(OperationCode::IAdd, value, Immediate(-diff_value));
583 aoffi.push_back(Operation(OperationCode::Select, condition, negative, value));
584 }
585 return aoffi;
586 }
587
588 for (std::size_t coord = 0; coord < coord_count; ++coord) {
589 s32 value = (*aoffi_immediate >> coord_offsets.at(coord)) & mask;
590 if (value >= wrap_value) {
591 value -= diff_value;
592 }
593 aoffi.push_back(Immediate(value));
594 }
595 return aoffi;
596}
597
598} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp
index c34843307..db15c0718 100644
--- a/src/video_core/shader/decode/xmad.cpp
+++ b/src/video_core/shader/decode/xmad.cpp
@@ -29,39 +29,55 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
29 const bool is_signed_b = instr.xmad.sign_b == 1; 29 const bool is_signed_b = instr.xmad.sign_b == 1;
30 const bool is_signed_c = is_signed_a; 30 const bool is_signed_c = is_signed_a;
31 31
32 auto [is_merge, op_b, op_c] = [&]() -> std::tuple<bool, Node, Node> { 32 auto [is_merge, is_psl, is_high_b, mode, op_b,
33 op_c] = [&]() -> std::tuple<bool, bool, bool, Tegra::Shader::XmadMode, Node, Node> {
33 switch (opcode->get().GetId()) { 34 switch (opcode->get().GetId()) {
34 case OpCode::Id::XMAD_CR: 35 case OpCode::Id::XMAD_CR:
35 return {instr.xmad.merge_56, 36 return {instr.xmad.merge_56,
37 instr.xmad.product_shift_left_second,
38 instr.xmad.high_b,
39 instr.xmad.mode_cbf,
36 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), 40 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()),
37 GetRegister(instr.gpr39)}; 41 GetRegister(instr.gpr39)};
38 case OpCode::Id::XMAD_RR: 42 case OpCode::Id::XMAD_RR:
39 return {instr.xmad.merge_37, GetRegister(instr.gpr20), GetRegister(instr.gpr39)}; 43 return {instr.xmad.merge_37, instr.xmad.product_shift_left, instr.xmad.high_b_rr,
44 instr.xmad.mode, GetRegister(instr.gpr20), GetRegister(instr.gpr39)};
40 case OpCode::Id::XMAD_RC: 45 case OpCode::Id::XMAD_RC:
41 return {false, GetRegister(instr.gpr39), 46 return {false,
47 false,
48 instr.xmad.high_b,
49 instr.xmad.mode_cbf,
50 GetRegister(instr.gpr39),
42 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; 51 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())};
43 case OpCode::Id::XMAD_IMM: 52 case OpCode::Id::XMAD_IMM:
44 return {instr.xmad.merge_37, Immediate(static_cast<u32>(instr.xmad.imm20_16)), 53 return {instr.xmad.merge_37,
54 instr.xmad.product_shift_left,
55 false,
56 instr.xmad.mode,
57 Immediate(static_cast<u32>(instr.xmad.imm20_16)),
45 GetRegister(instr.gpr39)}; 58 GetRegister(instr.gpr39)};
46 } 59 }
47 UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName()); 60 UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName());
48 return {false, Immediate(0), Immediate(0)}; 61 return {false, false, false, Tegra::Shader::XmadMode::None, Immediate(0), Immediate(0)};
49 }(); 62 }();
50 63
51 op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16); 64 op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16);
52 65
53 const Node original_b = op_b; 66 const Node original_b = op_b;
54 op_b = BitfieldExtract(op_b, instr.xmad.high_b ? 16 : 0, 16); 67 op_b = BitfieldExtract(op_b, is_high_b ? 16 : 0, 16);
55 68
56 // TODO(Rodrigo): Use an appropiate sign for this operation 69 // TODO(Rodrigo): Use an appropiate sign for this operation
57 Node product = Operation(OperationCode::IMul, NO_PRECISE, op_a, op_b); 70 Node product = Operation(OperationCode::IMul, NO_PRECISE, op_a, op_b);
58 if (instr.xmad.product_shift_left) { 71 if (is_psl) {
59 product = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, product, Immediate(16)); 72 product = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, product, Immediate(16));
60 } 73 }
74 SetTemporal(bb, 0, product);
75 product = GetTemporal(0);
61 76
62 const Node original_c = op_c; 77 const Node original_c = op_c;
78 const Tegra::Shader::XmadMode set_mode = mode; // Workaround to clang compile error
63 op_c = [&]() { 79 op_c = [&]() {
64 switch (instr.xmad.mode) { 80 switch (set_mode) {
65 case Tegra::Shader::XmadMode::None: 81 case Tegra::Shader::XmadMode::None:
66 return original_c; 82 return original_c;
67 case Tegra::Shader::XmadMode::CLo: 83 case Tegra::Shader::XmadMode::CLo:
@@ -80,8 +96,13 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
80 } 96 }
81 }(); 97 }();
82 98
99 SetTemporal(bb, 1, op_c);
100 op_c = GetTemporal(1);
101
83 // TODO(Rodrigo): Use an appropiate sign for this operation 102 // TODO(Rodrigo): Use an appropiate sign for this operation
84 Node sum = Operation(OperationCode::IAdd, product, op_c); 103 Node sum = Operation(OperationCode::IAdd, product, op_c);
104 SetTemporal(bb, 2, sum);
105 sum = GetTemporal(2);
85 if (is_merge) { 106 if (is_merge) {
86 const Node a = BitfieldExtract(sum, 0, 16); 107 const Node a = BitfieldExtract(sum, 0, 16);
87 const Node b = 108 const Node b =
@@ -95,4 +116,4 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
95 return pc; 116 return pc;
96} 117}
97 118
98} // namespace VideoCommon::Shader \ No newline at end of file 119} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 5bc3a3900..1afab08c0 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <cstring> 8#include <cstring>
9#include <map> 9#include <map>
10#include <optional>
10#include <set> 11#include <set>
11#include <string> 12#include <string>
12#include <tuple> 13#include <tuple>
@@ -275,6 +276,11 @@ struct GlobalMemoryBase {
275 } 276 }
276}; 277};
277 278
279struct GlobalMemoryUsage {
280 bool is_read{};
281 bool is_written{};
282};
283
278struct MetaArithmetic { 284struct MetaArithmetic {
279 bool precise{}; 285 bool precise{};
280}; 286};
@@ -290,6 +296,7 @@ struct MetaTexture {
290 const Sampler& sampler; 296 const Sampler& sampler;
291 Node array{}; 297 Node array{};
292 Node depth_compare{}; 298 Node depth_compare{};
299 std::vector<Node> aoffi;
293 Node bias{}; 300 Node bias{};
294 Node lod{}; 301 Node lod{};
295 Node component{}; 302 Node component{};
@@ -576,8 +583,8 @@ public:
576 return used_clip_distances; 583 return used_clip_distances;
577 } 584 }
578 585
579 const std::set<GlobalMemoryBase>& GetGlobalMemoryBases() const { 586 const std::map<GlobalMemoryBase, GlobalMemoryUsage>& GetGlobalMemory() const {
580 return used_global_memory_bases; 587 return used_global_memory;
581 } 588 }
582 589
583 std::size_t GetLength() const { 590 std::size_t GetLength() const {
@@ -741,14 +748,14 @@ private:
741 748
742 Node4 GetTexCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, 749 Node4 GetTexCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
743 Tegra::Shader::TextureProcessMode process_mode, bool depth_compare, 750 Tegra::Shader::TextureProcessMode process_mode, bool depth_compare,
744 bool is_array); 751 bool is_array, bool is_aoffi);
745 752
746 Node4 GetTexsCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, 753 Node4 GetTexsCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
747 Tegra::Shader::TextureProcessMode process_mode, bool depth_compare, 754 Tegra::Shader::TextureProcessMode process_mode, bool depth_compare,
748 bool is_array); 755 bool is_array);
749 756
750 Node4 GetTld4Code(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, 757 Node4 GetTld4Code(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
751 bool depth_compare, bool is_array); 758 bool depth_compare, bool is_array, bool is_aoffi);
752 759
753 Node4 GetTldsCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, 760 Node4 GetTldsCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
754 bool is_array); 761 bool is_array);
@@ -757,9 +764,11 @@ private:
757 Tegra::Shader::TextureType texture_type, bool depth_compare, bool is_array, 764 Tegra::Shader::TextureType texture_type, bool depth_compare, bool is_array,
758 bool lod_bias_enabled, std::size_t max_coords, std::size_t max_inputs); 765 bool lod_bias_enabled, std::size_t max_coords, std::size_t max_inputs);
759 766
767 std::vector<Node> GetAoffiCoordinates(Node aoffi_reg, std::size_t coord_count, bool is_tld4);
768
760 Node4 GetTextureCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, 769 Node4 GetTextureCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
761 Tegra::Shader::TextureProcessMode process_mode, std::vector<Node> coords, 770 Tegra::Shader::TextureProcessMode process_mode, std::vector<Node> coords,
762 Node array, Node depth_compare, u32 bias_offset); 771 Node array, Node depth_compare, u32 bias_offset, std::vector<Node> aoffi);
763 772
764 Node GetVideoOperand(Node op, bool is_chunk, bool is_signed, Tegra::Shader::VideoType type, 773 Node GetVideoOperand(Node op, bool is_chunk, bool is_signed, Tegra::Shader::VideoType type,
765 u64 byte_height); 774 u64 byte_height);
@@ -773,8 +782,15 @@ private:
773 782
774 Node TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor); 783 Node TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor);
775 784
785 std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor);
786
776 std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor); 787 std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor);
777 788
789 std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(NodeBlock& bb,
790 Node addr_register,
791 u32 immediate_offset,
792 bool is_write);
793
778 template <typename... T> 794 template <typename... T>
779 Node Operation(OperationCode code, const T*... operands) { 795 Node Operation(OperationCode code, const T*... operands) {
780 return StoreNode(OperationNode(code, operands...)); 796 return StoreNode(OperationNode(code, operands...));
@@ -828,7 +844,7 @@ private:
828 std::map<u32, ConstBuffer> used_cbufs; 844 std::map<u32, ConstBuffer> used_cbufs;
829 std::set<Sampler> used_samplers; 845 std::set<Sampler> used_samplers;
830 std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; 846 std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
831 std::set<GlobalMemoryBase> used_global_memory_bases; 847 std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
832 848
833 Tegra::Shader::Header header; 849 Tegra::Shader::Header header;
834}; 850};
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index 33b071747..4505667ff 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -6,6 +6,7 @@
6#include <utility> 6#include <utility>
7#include <variant> 7#include <variant>
8 8
9#include "common/common_types.h"
9#include "video_core/shader/shader_ir.h" 10#include "video_core/shader/shader_ir.h"
10 11
11namespace VideoCommon::Shader { 12namespace VideoCommon::Shader {
@@ -14,7 +15,7 @@ namespace {
14std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor, 15std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
15 OperationCode operation_code) { 16 OperationCode operation_code) {
16 for (; cursor >= 0; --cursor) { 17 for (; cursor >= 0; --cursor) {
17 const Node node = code[cursor]; 18 const Node node = code.at(cursor);
18 if (const auto operation = std::get_if<OperationNode>(node)) { 19 if (const auto operation = std::get_if<OperationNode>(node)) {
19 if (operation->GetCode() == operation_code) 20 if (operation->GetCode() == operation_code)
20 return {node, cursor}; 21 return {node, cursor};
@@ -64,6 +65,20 @@ Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) {
64 return nullptr; 65 return nullptr;
65} 66}
66 67
68std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) {
69 // Reduce the cursor in one to avoid infinite loops when the instruction sets the same register
70 // that it uses as operand
71 const auto [found, found_cursor] =
72 TrackRegister(&std::get<GprNode>(*tracked), code, cursor - 1);
73 if (!found) {
74 return {};
75 }
76 if (const auto immediate = std::get_if<ImmediateNode>(found)) {
77 return immediate->GetValue();
78 }
79 return {};
80}
81
67std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, 82std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code,
68 s64 cursor) { 83 s64 cursor) {
69 for (; cursor >= 0; --cursor) { 84 for (; cursor >= 0; --cursor) {
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index a7ac26d71..3b022a456 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -294,6 +294,8 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
294 return PixelFormat::Z16; 294 return PixelFormat::Z16;
295 case Tegra::Texture::TextureFormat::Z24S8: 295 case Tegra::Texture::TextureFormat::Z24S8:
296 return PixelFormat::Z24S8; 296 return PixelFormat::Z24S8;
297 case Tegra::Texture::TextureFormat::ZF32_X24S8:
298 return PixelFormat::Z32FS8;
297 case Tegra::Texture::TextureFormat::DXT1: 299 case Tegra::Texture::TextureFormat::DXT1:
298 return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1; 300 return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1;
299 case Tegra::Texture::TextureFormat::DXT23: 301 case Tegra::Texture::TextureFormat::DXT23:
diff --git a/src/video_core/texture_cache.cpp b/src/video_core/texture_cache.cpp
new file mode 100644
index 000000000..e96eba7cc
--- /dev/null
+++ b/src/video_core/texture_cache.cpp
@@ -0,0 +1,386 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/alignment.h"
6#include "common/assert.h"
7#include "common/cityhash.h"
8#include "common/common_types.h"
9#include "core/core.h"
10#include "video_core/surface.h"
11#include "video_core/texture_cache.h"
12#include "video_core/textures/decoders.h"
13#include "video_core/textures/texture.h"
14
15namespace VideoCommon {
16
17using VideoCore::Surface::SurfaceTarget;
18
19using VideoCore::Surface::ComponentTypeFromDepthFormat;
20using VideoCore::Surface::ComponentTypeFromRenderTarget;
21using VideoCore::Surface::ComponentTypeFromTexture;
22using VideoCore::Surface::PixelFormatFromDepthFormat;
23using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
24using VideoCore::Surface::PixelFormatFromTextureFormat;
25using VideoCore::Surface::SurfaceTargetFromTextureType;
26
27constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
28 return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
29}
30
31SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
32 const Tegra::Texture::FullTextureInfo& config) {
33 SurfaceParams params;
34 params.is_tiled = config.tic.IsTiled();
35 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
36 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
37 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
38 params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1;
39 params.pixel_format =
40 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), false);
41 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
42 params.type = GetFormatType(params.pixel_format);
43 params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
44 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
45 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
46 params.depth = config.tic.Depth();
47 if (params.target == SurfaceTarget::TextureCubemap ||
48 params.target == SurfaceTarget::TextureCubeArray) {
49 params.depth *= 6;
50 }
51 params.pitch = params.is_tiled ? 0 : config.tic.Pitch();
52 params.unaligned_height = config.tic.Height();
53 params.num_levels = config.tic.max_mip_level + 1;
54
55 params.CalculateCachedValues();
56 return params;
57}
58
59SurfaceParams SurfaceParams::CreateForDepthBuffer(
60 Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
61 u32 block_width, u32 block_height, u32 block_depth,
62 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
63 SurfaceParams params;
64 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
65 params.block_width = 1 << std::min(block_width, 5U);
66 params.block_height = 1 << std::min(block_height, 5U);
67 params.block_depth = 1 << std::min(block_depth, 5U);
68 params.tile_width_spacing = 1;
69 params.pixel_format = PixelFormatFromDepthFormat(format);
70 params.component_type = ComponentTypeFromDepthFormat(format);
71 params.type = GetFormatType(params.pixel_format);
72 params.width = zeta_width;
73 params.height = zeta_height;
74 params.unaligned_height = zeta_height;
75 params.target = SurfaceTarget::Texture2D;
76 params.depth = 1;
77 params.num_levels = 1;
78
79 params.CalculateCachedValues();
80 return params;
81}
82
83SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) {
84 const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
85 SurfaceParams params;
86 params.is_tiled =
87 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
88 params.block_width = 1 << config.memory_layout.block_width;
89 params.block_height = 1 << config.memory_layout.block_height;
90 params.block_depth = 1 << config.memory_layout.block_depth;
91 params.tile_width_spacing = 1;
92 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
93 params.component_type = ComponentTypeFromRenderTarget(config.format);
94 params.type = GetFormatType(params.pixel_format);
95 if (params.is_tiled) {
96 params.width = config.width;
97 } else {
98 const u32 bpp = GetFormatBpp(params.pixel_format) / CHAR_BIT;
99 params.pitch = config.width;
100 params.width = params.pitch / bpp;
101 }
102 params.height = config.height;
103 params.depth = 1;
104 params.unaligned_height = config.height;
105 params.target = SurfaceTarget::Texture2D;
106 params.num_levels = 1;
107
108 params.CalculateCachedValues();
109 return params;
110}
111
112SurfaceParams SurfaceParams::CreateForFermiCopySurface(
113 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
114 SurfaceParams params{};
115 params.is_tiled = !config.linear;
116 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
117 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
118 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
119 params.tile_width_spacing = 1;
120 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
121 params.component_type = ComponentTypeFromRenderTarget(config.format);
122 params.type = GetFormatType(params.pixel_format);
123 params.width = config.width;
124 params.height = config.height;
125 params.unaligned_height = config.height;
126 // TODO(Rodrigo): Try to guess the surface target from depth and layer parameters
127 params.target = SurfaceTarget::Texture2D;
128 params.depth = 1;
129 params.num_levels = 1;
130
131 params.CalculateCachedValues();
132 return params;
133}
134
135u32 SurfaceParams::GetMipWidth(u32 level) const {
136 return std::max(1U, width >> level);
137}
138
139u32 SurfaceParams::GetMipHeight(u32 level) const {
140 return std::max(1U, height >> level);
141}
142
143u32 SurfaceParams::GetMipDepth(u32 level) const {
144 return IsLayered() ? depth : std::max(1U, depth >> level);
145}
146
147bool SurfaceParams::IsLayered() const {
148 switch (target) {
149 case SurfaceTarget::Texture1DArray:
150 case SurfaceTarget::Texture2DArray:
151 case SurfaceTarget::TextureCubeArray:
152 case SurfaceTarget::TextureCubemap:
153 return true;
154 default:
155 return false;
156 }
157}
158
159u32 SurfaceParams::GetMipBlockHeight(u32 level) const {
160 // Auto block resizing algorithm from:
161 // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c
162 if (level == 0) {
163 return block_height;
164 }
165 const u32 height{GetMipHeight(level)};
166 const u32 default_block_height{GetDefaultBlockHeight(pixel_format)};
167 const u32 blocks_in_y{(height + default_block_height - 1) / default_block_height};
168 u32 block_height = 16;
169 while (block_height > 1 && blocks_in_y <= block_height * 4) {
170 block_height >>= 1;
171 }
172 return block_height;
173}
174
175u32 SurfaceParams::GetMipBlockDepth(u32 level) const {
176 if (level == 0)
177 return block_depth;
178 if (target != SurfaceTarget::Texture3D)
179 return 1;
180
181 const u32 depth{GetMipDepth(level)};
182 u32 block_depth = 32;
183 while (block_depth > 1 && depth * 2 <= block_depth) {
184 block_depth >>= 1;
185 }
186 if (block_depth == 32 && GetMipBlockHeight(level) >= 4) {
187 return 16;
188 }
189 return block_depth;
190}
191
192std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const {
193 std::size_t offset = 0;
194 for (u32 i = 0; i < level; i++) {
195 offset += GetInnerMipmapMemorySize(i, false, IsLayered(), false);
196 }
197 return offset;
198}
199
200std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const {
201 std::size_t offset = 0;
202 for (u32 i = 0; i < level; i++) {
203 offset += GetInnerMipmapMemorySize(i, true, false, false);
204 }
205 return offset;
206}
207
208std::size_t SurfaceParams::GetGuestLayerSize() const {
209 return GetInnerMemorySize(false, true, false);
210}
211
212std::size_t SurfaceParams::GetHostLayerSize(u32 level) const {
213 return GetInnerMipmapMemorySize(level, true, IsLayered(), false);
214}
215
216bool SurfaceParams::IsFamiliar(const SurfaceParams& view_params) const {
217 if (std::tie(is_tiled, tile_width_spacing, pixel_format, component_type, type) !=
218 std::tie(view_params.is_tiled, view_params.tile_width_spacing, view_params.pixel_format,
219 view_params.component_type, view_params.type)) {
220 return false;
221 }
222
223 const SurfaceTarget view_target{view_params.target};
224 if (view_target == target) {
225 return true;
226 }
227
228 switch (target) {
229 case SurfaceTarget::Texture1D:
230 case SurfaceTarget::Texture2D:
231 case SurfaceTarget::Texture3D:
232 return false;
233 case SurfaceTarget::Texture1DArray:
234 return view_target == SurfaceTarget::Texture1D;
235 case SurfaceTarget::Texture2DArray:
236 return view_target == SurfaceTarget::Texture2D;
237 case SurfaceTarget::TextureCubemap:
238 return view_target == SurfaceTarget::Texture2D ||
239 view_target == SurfaceTarget::Texture2DArray;
240 case SurfaceTarget::TextureCubeArray:
241 return view_target == SurfaceTarget::Texture2D ||
242 view_target == SurfaceTarget::Texture2DArray ||
243 view_target == SurfaceTarget::TextureCubemap;
244 default:
245 UNIMPLEMENTED_MSG("Unimplemented texture family={}", static_cast<u32>(target));
246 return false;
247 }
248}
249
250bool SurfaceParams::IsPixelFormatZeta() const {
251 return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat &&
252 pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;
253}
254
255void SurfaceParams::CalculateCachedValues() {
256 guest_size_in_bytes = GetInnerMemorySize(false, false, false);
257
258 // ASTC is uncompressed in software, in emulated as RGBA8
259 if (IsPixelFormatASTC(pixel_format)) {
260 host_size_in_bytes = width * height * depth * 4;
261 } else {
262 host_size_in_bytes = GetInnerMemorySize(true, false, false);
263 }
264
265 switch (target) {
266 case SurfaceTarget::Texture1D:
267 case SurfaceTarget::Texture2D:
268 case SurfaceTarget::Texture3D:
269 num_layers = 1;
270 break;
271 case SurfaceTarget::Texture1DArray:
272 case SurfaceTarget::Texture2DArray:
273 case SurfaceTarget::TextureCubemap:
274 case SurfaceTarget::TextureCubeArray:
275 num_layers = depth;
276 break;
277 default:
278 UNREACHABLE();
279 }
280}
281
282std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only,
283 bool uncompressed) const {
284 const bool tiled{as_host_size ? false : is_tiled};
285 const u32 tile_x{GetDefaultBlockWidth(pixel_format)};
286 const u32 tile_y{GetDefaultBlockHeight(pixel_format)};
287 const u32 width{GetMipmapSize(uncompressed, GetMipWidth(level), tile_x)};
288 const u32 height{GetMipmapSize(uncompressed, GetMipHeight(level), tile_y)};
289 const u32 depth{layer_only ? 1U : GetMipDepth(level)};
290 return Tegra::Texture::CalculateSize(tiled, GetBytesPerPixel(pixel_format), width, height,
291 depth, GetMipBlockHeight(level), GetMipBlockDepth(level));
292}
293
294std::size_t SurfaceParams::GetInnerMemorySize(bool as_host_size, bool layer_only,
295 bool uncompressed) const {
296 std::size_t size = 0;
297 for (u32 level = 0; level < num_levels; ++level) {
298 size += GetInnerMipmapMemorySize(level, as_host_size, layer_only, uncompressed);
299 }
300 if (!as_host_size && is_tiled) {
301 size = Common::AlignUp(size, Tegra::Texture::GetGOBSize() * block_height * block_depth);
302 }
303 return size;
304}
305
306std::map<u64, std::pair<u32, u32>> SurfaceParams::CreateViewOffsetMap() const {
307 std::map<u64, std::pair<u32, u32>> view_offset_map;
308 switch (target) {
309 case SurfaceTarget::Texture1D:
310 case SurfaceTarget::Texture2D:
311 case SurfaceTarget::Texture3D: {
312 constexpr u32 layer = 0;
313 for (u32 level = 0; level < num_levels; ++level) {
314 const std::size_t offset{GetGuestMipmapLevelOffset(level)};
315 view_offset_map.insert({offset, {layer, level}});
316 }
317 break;
318 }
319 case SurfaceTarget::Texture1DArray:
320 case SurfaceTarget::Texture2DArray:
321 case SurfaceTarget::TextureCubemap:
322 case SurfaceTarget::TextureCubeArray: {
323 const std::size_t layer_size{GetGuestLayerSize()};
324 for (u32 level = 0; level < num_levels; ++level) {
325 const std::size_t level_offset{GetGuestMipmapLevelOffset(level)};
326 for (u32 layer = 0; layer < num_layers; ++layer) {
327 const auto layer_offset{static_cast<std::size_t>(layer_size * layer)};
328 const std::size_t offset{level_offset + layer_offset};
329 view_offset_map.insert({offset, {layer, level}});
330 }
331 }
332 break;
333 }
334 default:
335 UNIMPLEMENTED_MSG("Unimplemented surface target {}", static_cast<u32>(target));
336 }
337 return view_offset_map;
338}
339
340bool SurfaceParams::IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const {
341 return IsDimensionValid(view_params, level) && IsDepthValid(view_params, level) &&
342 IsInBounds(view_params, layer, level);
343}
344
345bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const {
346 return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level);
347}
348
349bool SurfaceParams::IsDepthValid(const SurfaceParams& view_params, u32 level) const {
350 if (view_params.target != SurfaceTarget::Texture3D) {
351 return true;
352 }
353 return view_params.depth == GetMipDepth(level);
354}
355
356bool SurfaceParams::IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const {
357 return layer + view_params.num_layers <= num_layers &&
358 level + view_params.num_levels <= num_levels;
359}
360
361std::size_t HasheableSurfaceParams::Hash() const {
362 return static_cast<std::size_t>(
363 Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
364}
365
366bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const {
367 return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width,
368 height, depth, pitch, unaligned_height, num_levels, pixel_format,
369 component_type, type, target) ==
370 std::tie(rhs.is_tiled, rhs.block_width, rhs.block_height, rhs.block_depth,
371 rhs.tile_width_spacing, rhs.width, rhs.height, rhs.depth, rhs.pitch,
372 rhs.unaligned_height, rhs.num_levels, rhs.pixel_format, rhs.component_type,
373 rhs.type, rhs.target);
374}
375
376std::size_t ViewKey::Hash() const {
377 return static_cast<std::size_t>(
378 Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
379}
380
381bool ViewKey::operator==(const ViewKey& rhs) const {
382 return std::tie(base_layer, num_layers, base_level, num_levels) ==
383 std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels);
384}
385
386} // namespace VideoCommon
diff --git a/src/video_core/texture_cache.h b/src/video_core/texture_cache.h
new file mode 100644
index 000000000..041551691
--- /dev/null
+++ b/src/video_core/texture_cache.h
@@ -0,0 +1,586 @@
1// Copyright 2019 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 <list>
8#include <memory>
9#include <set>
10#include <tuple>
11#include <type_traits>
12#include <unordered_map>
13
14#include <boost/icl/interval_map.hpp>
15#include <boost/range/iterator_range.hpp>
16
17#include "common/assert.h"
18#include "common/common_types.h"
19#include "core/memory.h"
20#include "video_core/engines/fermi_2d.h"
21#include "video_core/engines/maxwell_3d.h"
22#include "video_core/gpu.h"
23#include "video_core/rasterizer_interface.h"
24#include "video_core/surface.h"
25
26namespace Core {
27class System;
28}
29
30namespace Tegra::Texture {
31struct FullTextureInfo;
32}
33
34namespace VideoCore {
35class RasterizerInterface;
36}
37
38namespace VideoCommon {
39
40class HasheableSurfaceParams {
41public:
42 std::size_t Hash() const;
43
44 bool operator==(const HasheableSurfaceParams& rhs) const;
45
46protected:
47 // Avoid creation outside of a managed environment.
48 HasheableSurfaceParams() = default;
49
50 bool is_tiled;
51 u32 block_width;
52 u32 block_height;
53 u32 block_depth;
54 u32 tile_width_spacing;
55 u32 width;
56 u32 height;
57 u32 depth;
58 u32 pitch;
59 u32 unaligned_height;
60 u32 num_levels;
61 VideoCore::Surface::PixelFormat pixel_format;
62 VideoCore::Surface::ComponentType component_type;
63 VideoCore::Surface::SurfaceType type;
64 VideoCore::Surface::SurfaceTarget target;
65};
66
67class SurfaceParams final : public HasheableSurfaceParams {
68public:
69 /// Creates SurfaceCachedParams from a texture configuration.
70 static SurfaceParams CreateForTexture(Core::System& system,
71 const Tegra::Texture::FullTextureInfo& config);
72
73 /// Creates SurfaceCachedParams for a depth buffer configuration.
74 static SurfaceParams CreateForDepthBuffer(
75 Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
76 u32 block_width, u32 block_height, u32 block_depth,
77 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
78
79 /// Creates SurfaceCachedParams from a framebuffer configuration.
80 static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
81
82 /// Creates SurfaceCachedParams from a Fermi2D surface configuration.
83 static SurfaceParams CreateForFermiCopySurface(
84 const Tegra::Engines::Fermi2D::Regs::Surface& config);
85
86 bool IsTiled() const {
87 return is_tiled;
88 }
89
90 u32 GetBlockWidth() const {
91 return block_width;
92 }
93
94 u32 GetTileWidthSpacing() const {
95 return tile_width_spacing;
96 }
97
98 u32 GetWidth() const {
99 return width;
100 }
101
102 u32 GetHeight() const {
103 return height;
104 }
105
106 u32 GetDepth() const {
107 return depth;
108 }
109
110 u32 GetPitch() const {
111 return pitch;
112 }
113
114 u32 GetNumLevels() const {
115 return num_levels;
116 }
117
118 VideoCore::Surface::PixelFormat GetPixelFormat() const {
119 return pixel_format;
120 }
121
122 VideoCore::Surface::ComponentType GetComponentType() const {
123 return component_type;
124 }
125
126 VideoCore::Surface::SurfaceTarget GetTarget() const {
127 return target;
128 }
129
130 VideoCore::Surface::SurfaceType GetType() const {
131 return type;
132 }
133
134 std::size_t GetGuestSizeInBytes() const {
135 return guest_size_in_bytes;
136 }
137
138 std::size_t GetHostSizeInBytes() const {
139 return host_size_in_bytes;
140 }
141
142 u32 GetNumLayers() const {
143 return num_layers;
144 }
145
146 /// Returns the width of a given mipmap level.
147 u32 GetMipWidth(u32 level) const;
148
149 /// Returns the height of a given mipmap level.
150 u32 GetMipHeight(u32 level) const;
151
152 /// Returns the depth of a given mipmap level.
153 u32 GetMipDepth(u32 level) const;
154
155 /// Returns true if these parameters are from a layered surface.
156 bool IsLayered() const;
157
158 /// Returns the block height of a given mipmap level.
159 u32 GetMipBlockHeight(u32 level) const;
160
161 /// Returns the block depth of a given mipmap level.
162 u32 GetMipBlockDepth(u32 level) const;
163
164 /// Returns the offset in bytes in guest memory of a given mipmap level.
165 std::size_t GetGuestMipmapLevelOffset(u32 level) const;
166
167 /// Returns the offset in bytes in host memory (linear) of a given mipmap level.
168 std::size_t GetHostMipmapLevelOffset(u32 level) const;
169
170 /// Returns the size of a layer in bytes in guest memory.
171 std::size_t GetGuestLayerSize() const;
172
173 /// Returns the size of a layer in bytes in host memory for a given mipmap level.
174 std::size_t GetHostLayerSize(u32 level) const;
175
176 /// Returns true if another surface can be familiar with this. This is a loosely defined term
177 /// that reflects the possibility of these two surface parameters potentially being part of a
178 /// bigger superset.
179 bool IsFamiliar(const SurfaceParams& view_params) const;
180
181 /// Returns true if the pixel format is a depth and/or stencil format.
182 bool IsPixelFormatZeta() const;
183
184 /// Creates a map that redirects an address difference to a layer and mipmap level.
185 std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const;
186
187 /// Returns true if the passed surface view parameters is equal or a valid subset of this.
188 bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const;
189
190private:
191 /// Calculates values that can be deduced from HasheableSurfaceParams.
192 void CalculateCachedValues();
193
194 /// Returns the size of a given mipmap level.
195 std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only,
196 bool uncompressed) const;
197
198 /// Returns the size of all mipmap levels and aligns as needed.
199 std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const;
200
201 /// Returns true if the passed view width and height match the size of this params in a given
202 /// mipmap level.
203 bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const;
204
205 /// Returns true if the passed view depth match the size of this params in a given mipmap level.
206 bool IsDepthValid(const SurfaceParams& view_params, u32 level) const;
207
208 /// Returns true if the passed view layers and mipmap levels are in bounds.
209 bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const;
210
211 std::size_t guest_size_in_bytes;
212 std::size_t host_size_in_bytes;
213 u32 num_layers;
214};
215
216struct ViewKey {
217 std::size_t Hash() const;
218
219 bool operator==(const ViewKey& rhs) const;
220
221 u32 base_layer{};
222 u32 num_layers{};
223 u32 base_level{};
224 u32 num_levels{};
225};
226
227} // namespace VideoCommon
228
229namespace std {
230
231template <>
232struct hash<VideoCommon::SurfaceParams> {
233 std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept {
234 return k.Hash();
235 }
236};
237
238template <>
239struct hash<VideoCommon::ViewKey> {
240 std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept {
241 return k.Hash();
242 }
243};
244
245} // namespace std
246
247namespace VideoCommon {
248
249template <typename TView, typename TExecutionContext>
250class SurfaceBase {
251 static_assert(std::is_trivially_copyable_v<TExecutionContext>);
252
253public:
254 virtual void LoadBuffer() = 0;
255
256 virtual TExecutionContext FlushBuffer(TExecutionContext exctx) = 0;
257
258 virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0;
259
260 TView* TryGetView(VAddr view_addr, const SurfaceParams& view_params) {
261 if (view_addr < cpu_addr || !params.IsFamiliar(view_params)) {
262 // It can't be a view if it's in a prior address.
263 return {};
264 }
265
266 const auto relative_offset{static_cast<u64>(view_addr - cpu_addr)};
267 const auto it{view_offset_map.find(relative_offset)};
268 if (it == view_offset_map.end()) {
269 // Couldn't find an aligned view.
270 return {};
271 }
272 const auto [layer, level] = it->second;
273
274 if (!params.IsViewValid(view_params, layer, level)) {
275 return {};
276 }
277
278 return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels());
279 }
280
281 VAddr GetCpuAddr() const {
282 ASSERT(is_registered);
283 return cpu_addr;
284 }
285
286 u8* GetHostPtr() const {
287 ASSERT(is_registered);
288 return host_ptr;
289 }
290
291 CacheAddr GetCacheAddr() const {
292 ASSERT(is_registered);
293 return cache_addr;
294 }
295
296 std::size_t GetSizeInBytes() const {
297 return params.GetGuestSizeInBytes();
298 }
299
300 void MarkAsModified(bool is_modified_) {
301 is_modified = is_modified_;
302 }
303
304 const SurfaceParams& GetSurfaceParams() const {
305 return params;
306 }
307
308 TView* GetView(VAddr view_addr, const SurfaceParams& view_params) {
309 TView* view{TryGetView(view_addr, view_params)};
310 ASSERT(view != nullptr);
311 return view;
312 }
313
314 void Register(VAddr cpu_addr_, u8* host_ptr_) {
315 ASSERT(!is_registered);
316 is_registered = true;
317 cpu_addr = cpu_addr_;
318 host_ptr = host_ptr_;
319 cache_addr = ToCacheAddr(host_ptr_);
320 }
321
322 void Register(VAddr cpu_addr_) {
323 Register(cpu_addr_, Memory::GetPointer(cpu_addr_));
324 }
325
326 void Unregister() {
327 ASSERT(is_registered);
328 is_registered = false;
329 }
330
331 bool IsRegistered() const {
332 return is_registered;
333 }
334
335protected:
336 explicit SurfaceBase(const SurfaceParams& params)
337 : params{params}, view_offset_map{params.CreateViewOffsetMap()} {}
338
339 ~SurfaceBase() = default;
340
341 virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0;
342
343 bool IsModified() const {
344 return is_modified;
345 }
346
347 const SurfaceParams params;
348
349private:
350 TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) {
351 const ViewKey key{base_layer, num_layers, base_level, num_levels};
352 const auto [entry, is_cache_miss] = views.try_emplace(key);
353 auto& view{entry->second};
354 if (is_cache_miss) {
355 view = CreateView(key);
356 }
357 return view.get();
358 }
359
360 const std::map<u64, std::pair<u32, u32>> view_offset_map;
361
362 VAddr cpu_addr{};
363 u8* host_ptr{};
364 CacheAddr cache_addr{};
365 bool is_modified{};
366 bool is_registered{};
367 std::unordered_map<ViewKey, std::unique_ptr<TView>> views;
368};
369
370template <typename TSurface, typename TView, typename TExecutionContext>
371class TextureCache {
372 static_assert(std::is_trivially_copyable_v<TExecutionContext>);
373 using ResultType = std::tuple<TView*, TExecutionContext>;
374 using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface*>>;
375 using IntervalType = typename IntervalMap::interval_type;
376
377public:
378 void InvalidateRegion(CacheAddr addr, std::size_t size) {
379 for (TSurface* surface : GetSurfacesInRegion(addr, size)) {
380 if (!surface->IsRegistered()) {
381 // Skip duplicates
382 continue;
383 }
384 Unregister(surface);
385 }
386 }
387
388 ResultType GetTextureSurface(TExecutionContext exctx,
389 const Tegra::Texture::FullTextureInfo& config) {
390 auto& memory_manager{system.GPU().MemoryManager()};
391 const auto cpu_addr{memory_manager.GpuToCpuAddress(config.tic.Address())};
392 if (!cpu_addr) {
393 return {{}, exctx};
394 }
395 const auto params{SurfaceParams::CreateForTexture(system, config)};
396 return GetSurfaceView(exctx, *cpu_addr, params, true);
397 }
398
399 ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) {
400 const auto& regs{system.GPU().Maxwell3D().regs};
401 if (!regs.zeta.Address() || !regs.zeta_enable) {
402 return {{}, exctx};
403 }
404
405 auto& memory_manager{system.GPU().MemoryManager()};
406 const auto cpu_addr{memory_manager.GpuToCpuAddress(regs.zeta.Address())};
407 if (!cpu_addr) {
408 return {{}, exctx};
409 }
410
411 const auto depth_params{SurfaceParams::CreateForDepthBuffer(
412 system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
413 regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
414 regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
415 return GetSurfaceView(exctx, *cpu_addr, depth_params, preserve_contents);
416 }
417
418 ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index,
419 bool preserve_contents) {
420 ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
421
422 const auto& regs{system.GPU().Maxwell3D().regs};
423 if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
424 regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
425 return {{}, exctx};
426 }
427
428 auto& memory_manager{system.GPU().MemoryManager()};
429 const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
430 const auto cpu_addr{memory_manager.GpuToCpuAddress(
431 config.Address() + config.base_layer * config.layer_stride * sizeof(u32))};
432 if (!cpu_addr) {
433 return {{}, exctx};
434 }
435
436 return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
437 preserve_contents);
438 }
439
440 ResultType GetFermiSurface(TExecutionContext exctx,
441 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
442 const auto cpu_addr{system.GPU().MemoryManager().GpuToCpuAddress(config.Address())};
443 ASSERT(cpu_addr);
444 return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFermiCopySurface(config),
445 true);
446 }
447
448 TSurface* TryFindFramebufferSurface(const u8* host_ptr) const {
449 const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))};
450 return it != registered_surfaces.end() ? *it->second.begin() : nullptr;
451 }
452
453protected:
454 TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
455 : system{system}, rasterizer{rasterizer} {}
456
457 ~TextureCache() = default;
458
459 virtual ResultType TryFastGetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr,
460 const SurfaceParams& params, bool preserve_contents,
461 const std::vector<TSurface*>& overlaps) = 0;
462
463 virtual std::unique_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0;
464
465 void Register(TSurface* surface, VAddr cpu_addr, u8* host_ptr) {
466 surface->Register(cpu_addr, host_ptr);
467 registered_surfaces.add({GetSurfaceInterval(surface), {surface}});
468 rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1);
469 }
470
471 void Unregister(TSurface* surface) {
472 registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}});
473 rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1);
474 surface->Unregister();
475 }
476
477 TSurface* GetUncachedSurface(const SurfaceParams& params) {
478 if (TSurface* surface = TryGetReservedSurface(params); surface)
479 return surface;
480 // No reserved surface available, create a new one and reserve it
481 auto new_surface{CreateSurface(params)};
482 TSurface* surface{new_surface.get()};
483 ReserveSurface(params, std::move(new_surface));
484 return surface;
485 }
486
487 Core::System& system;
488
489private:
490 ResultType GetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, const SurfaceParams& params,
491 bool preserve_contents) {
492 const auto host_ptr{Memory::GetPointer(cpu_addr)};
493 const auto cache_addr{ToCacheAddr(host_ptr)};
494 const auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())};
495 if (overlaps.empty()) {
496 return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents);
497 }
498
499 if (overlaps.size() == 1) {
500 if (TView* view = overlaps[0]->TryGetView(cpu_addr, params); view)
501 return {view, exctx};
502 }
503
504 TView* fast_view;
505 std::tie(fast_view, exctx) =
506 TryFastGetSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents, overlaps);
507
508 for (TSurface* surface : overlaps) {
509 if (!fast_view) {
510 // Flush even when we don't care about the contents, to preserve memory not written
511 // by the new surface.
512 exctx = surface->FlushBuffer(exctx);
513 }
514 Unregister(surface);
515 }
516
517 if (fast_view) {
518 return {fast_view, exctx};
519 }
520
521 return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents);
522 }
523
524 ResultType LoadSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr,
525 const SurfaceParams& params, bool preserve_contents) {
526 TSurface* new_surface{GetUncachedSurface(params)};
527 Register(new_surface, cpu_addr, host_ptr);
528 if (preserve_contents) {
529 exctx = LoadSurface(exctx, new_surface);
530 }
531 return {new_surface->GetView(cpu_addr, params), exctx};
532 }
533
534 TExecutionContext LoadSurface(TExecutionContext exctx, TSurface* surface) {
535 surface->LoadBuffer();
536 exctx = surface->UploadTexture(exctx);
537 surface->MarkAsModified(false);
538 return exctx;
539 }
540
541 std::vector<TSurface*> GetSurfacesInRegion(CacheAddr cache_addr, std::size_t size) const {
542 if (size == 0) {
543 return {};
544 }
545 const IntervalType interval{cache_addr, cache_addr + size};
546
547 std::vector<TSurface*> surfaces;
548 for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) {
549 surfaces.push_back(*pair.second.begin());
550 }
551 return surfaces;
552 }
553
554 void ReserveSurface(const SurfaceParams& params, std::unique_ptr<TSurface> surface) {
555 surface_reserve[params].push_back(std::move(surface));
556 }
557
558 TSurface* TryGetReservedSurface(const SurfaceParams& params) {
559 auto search{surface_reserve.find(params)};
560 if (search == surface_reserve.end()) {
561 return {};
562 }
563 for (auto& surface : search->second) {
564 if (!surface->IsRegistered()) {
565 return surface.get();
566 }
567 }
568 return {};
569 }
570
571 IntervalType GetSurfaceInterval(TSurface* surface) const {
572 return IntervalType::right_open(surface->GetCacheAddr(),
573 surface->GetCacheAddr() + surface->GetSizeInBytes());
574 }
575
576 VideoCore::RasterizerInterface& rasterizer;
577
578 IntervalMap registered_surfaces;
579
580 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
581 /// previously been used. This is to prevent surfaces from being constantly created and
582 /// destroyed when used with different surface parameters.
583 std::unordered_map<SurfaceParams, std::list<std::unique_ptr<TSurface>>> surface_reserve;
584};
585
586} // namespace VideoCommon
diff --git a/src/video_core/textures/convert.cpp b/src/video_core/textures/convert.cpp
index 5e439f036..82050bd51 100644
--- a/src/video_core/textures/convert.cpp
+++ b/src/video_core/textures/convert.cpp
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "video_core/surface.h"
13#include "video_core/textures/astc.h" 14#include "video_core/textures/astc.h"
14#include "video_core/textures/convert.h" 15#include "video_core/textures/convert.h"
15 16
diff --git a/src/video_core/textures/convert.h b/src/video_core/textures/convert.h
index 07cd8b5da..12542e71c 100644
--- a/src/video_core/textures/convert.h
+++ b/src/video_core/textures/convert.h
@@ -5,7 +5,10 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "video_core/surface.h" 8
9namespace VideoCore::Surface {
10enum class PixelFormat;
11}
9 12
10namespace Tegra::Texture { 13namespace Tegra::Texture {
11 14
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 93ecc6e31..bea0d5bc2 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -7,9 +7,7 @@
7#include <array> 7#include <array>
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "video_core/memory_manager.h"
13 11
14namespace Tegra::Texture { 12namespace Tegra::Texture {
15 13
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 4cab599b4..31b65c04c 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -31,6 +31,8 @@ add_executable(yuzu
31 configuration/configure_general.h 31 configuration/configure_general.h
32 configuration/configure_graphics.cpp 32 configuration/configure_graphics.cpp
33 configuration/configure_graphics.h 33 configuration/configure_graphics.h
34 configuration/configure_hotkeys.cpp
35 configuration/configure_hotkeys.h
34 configuration/configure_input.cpp 36 configuration/configure_input.cpp
35 configuration/configure_input.h 37 configuration/configure_input.h
36 configuration/configure_input_player.cpp 38 configuration/configure_input_player.cpp
@@ -54,8 +56,6 @@ add_executable(yuzu
54 debugger/graphics/graphics_breakpoints.cpp 56 debugger/graphics/graphics_breakpoints.cpp
55 debugger/graphics/graphics_breakpoints.h 57 debugger/graphics/graphics_breakpoints.h
56 debugger/graphics/graphics_breakpoints_p.h 58 debugger/graphics/graphics_breakpoints_p.h
57 debugger/graphics/graphics_surface.cpp
58 debugger/graphics/graphics_surface.h
59 debugger/console.cpp 59 debugger/console.cpp
60 debugger/console.h 60 debugger/console.h
61 debugger/profiler.cpp 61 debugger/profiler.cpp
@@ -78,6 +78,8 @@ add_executable(yuzu
78 ui_settings.h 78 ui_settings.h
79 util/limitable_input_dialog.cpp 79 util/limitable_input_dialog.cpp
80 util/limitable_input_dialog.h 80 util/limitable_input_dialog.h
81 util/sequence_dialog/sequence_dialog.cpp
82 util/sequence_dialog/sequence_dialog.h
81 util/spinbox.cpp 83 util/spinbox.cpp
82 util/spinbox.h 84 util/spinbox.h
83 util/util.cpp 85 util/util.cpp
@@ -95,6 +97,7 @@ set(UIS
95 configuration/configure_gamelist.ui 97 configuration/configure_gamelist.ui
96 configuration/configure_general.ui 98 configuration/configure_general.ui
97 configuration/configure_graphics.ui 99 configuration/configure_graphics.ui
100 configuration/configure_hotkeys.ui
98 configuration/configure_input.ui 101 configuration/configure_input.ui
99 configuration/configure_input_player.ui 102 configuration/configure_input_player.ui
100 configuration/configure_input_simple.ui 103 configuration/configure_input_simple.ui
@@ -105,7 +108,6 @@ set(UIS
105 configuration/configure_touchscreen_advanced.ui 108 configuration/configure_touchscreen_advanced.ui
106 configuration/configure_web.ui 109 configuration/configure_web.ui
107 compatdb.ui 110 compatdb.ui
108 hotkeys.ui
109 loading_screen.ui 111 loading_screen.ui
110 main.ui 112 main.ui
111) 113)
@@ -149,6 +151,12 @@ target_link_libraries(yuzu PRIVATE common core input_common video_core)
149target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) 151target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
150target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 152target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
151 153
154target_compile_definitions(yuzu PRIVATE
155 # Use QStringBuilder for string concatenation to reduce
156 # the overall number of temporary strings created.
157 -DQT_USE_QSTRINGBUILDER
158)
159
152if (YUZU_ENABLE_COMPATIBILITY_REPORTING) 160if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
153 target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING) 161 target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING)
154endif() 162endif()
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
index f95f7fe3c..743b24d76 100644
--- a/src/yuzu/applets/profile_select.cpp
+++ b/src/yuzu/applets/profile_select.cpp
@@ -4,6 +4,7 @@
4 4
5#include <mutex> 5#include <mutex>
6#include <QDialogButtonBox> 6#include <QDialogButtonBox>
7#include <QHeaderView>
7#include <QLabel> 8#include <QLabel>
8#include <QLineEdit> 9#include <QLineEdit>
9#include <QScrollArea> 10#include <QScrollArea>
diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h
index 868573324..1c2922e54 100644
--- a/src/yuzu/applets/profile_select.h
+++ b/src/yuzu/applets/profile_select.h
@@ -7,6 +7,7 @@
7#include <vector> 7#include <vector>
8#include <QDialog> 8#include <QDialog>
9#include <QList> 9#include <QList>
10#include <QTreeView>
10#include "core/frontend/applets/profile_select.h" 11#include "core/frontend/applets/profile_select.h"
11 12
12class GMainWindow; 13class GMainWindow;
@@ -16,7 +17,6 @@ class QLabel;
16class QScrollArea; 17class QScrollArea;
17class QStandardItem; 18class QStandardItem;
18class QStandardItemModel; 19class QStandardItemModel;
19class QTreeView;
20class QVBoxLayout; 20class QVBoxLayout;
21 21
22class QtProfileSelectionDialog final : public QDialog { 22class QtProfileSelectionDialog final : public QDialog {
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 7438fbc0a..c29f2d2dc 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -1,6 +1,13 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#include <QApplication> 5#include <QApplication>
2#include <QHBoxLayout> 6#include <QHBoxLayout>
3#include <QKeyEvent> 7#include <QKeyEvent>
8#include <QOffscreenSurface>
9#include <QOpenGLWindow>
10#include <QPainter>
4#include <QScreen> 11#include <QScreen>
5#include <QWindow> 12#include <QWindow>
6#include <fmt/format.h> 13#include <fmt/format.h>
@@ -82,13 +89,36 @@ void EmuThread::run() {
82 render_window->moveContext(); 89 render_window->moveContext();
83} 90}
84 91
92class GGLContext : public Core::Frontend::GraphicsContext {
93public:
94 explicit GGLContext(QOpenGLContext* shared_context) : surface() {
95 context = std::make_unique<QOpenGLContext>(shared_context);
96 surface.setFormat(shared_context->format());
97 surface.create();
98 }
99
100 void MakeCurrent() override {
101 context->makeCurrent(&surface);
102 }
103
104 void DoneCurrent() override {
105 context->doneCurrent();
106 }
107
108 void SwapBuffers() override {}
109
110private:
111 std::unique_ptr<QOpenGLContext> context;
112 QOffscreenSurface surface;
113};
114
85// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL 115// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
86// context. 116// context.
87// The corresponding functionality is handled in EmuThread instead 117// The corresponding functionality is handled in EmuThread instead
88class GGLWidgetInternal : public QGLWidget { 118class GGLWidgetInternal : public QOpenGLWindow {
89public: 119public:
90 GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) 120 GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
91 : QGLWidget(fmt, parent), parent(parent) {} 121 : QOpenGLWindow(shared_context), parent(parent) {}
92 122
93 void paintEvent(QPaintEvent* ev) override { 123 void paintEvent(QPaintEvent* ev) override {
94 if (do_painting) { 124 if (do_painting) {
@@ -101,9 +131,51 @@ public:
101 parent->OnFramebufferSizeChanged(); 131 parent->OnFramebufferSizeChanged();
102 } 132 }
103 133
134 void keyPressEvent(QKeyEvent* event) override {
135 InputCommon::GetKeyboard()->PressKey(event->key());
136 }
137
138 void keyReleaseEvent(QKeyEvent* event) override {
139 InputCommon::GetKeyboard()->ReleaseKey(event->key());
140 }
141
142 void mousePressEvent(QMouseEvent* event) override {
143 if (event->source() == Qt::MouseEventSynthesizedBySystem)
144 return; // touch input is handled in TouchBeginEvent
145
146 const auto pos{event->pos()};
147 if (event->button() == Qt::LeftButton) {
148 const auto [x, y] = parent->ScaleTouch(pos);
149 parent->TouchPressed(x, y);
150 } else if (event->button() == Qt::RightButton) {
151 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
152 }
153 }
154
155 void mouseMoveEvent(QMouseEvent* event) override {
156 if (event->source() == Qt::MouseEventSynthesizedBySystem)
157 return; // touch input is handled in TouchUpdateEvent
158
159 const auto pos{event->pos()};
160 const auto [x, y] = parent->ScaleTouch(pos);
161 parent->TouchMoved(x, y);
162 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
163 }
164
165 void mouseReleaseEvent(QMouseEvent* event) override {
166 if (event->source() == Qt::MouseEventSynthesizedBySystem)
167 return; // touch input is handled in TouchEndEvent
168
169 if (event->button() == Qt::LeftButton)
170 parent->TouchReleased();
171 else if (event->button() == Qt::RightButton)
172 InputCommon::GetMotionEmu()->EndTilt();
173 }
174
104 void DisablePainting() { 175 void DisablePainting() {
105 do_painting = false; 176 do_painting = false;
106 } 177 }
178
107 void EnablePainting() { 179 void EnablePainting() {
108 do_painting = true; 180 do_painting = true;
109 } 181 }
@@ -114,7 +186,7 @@ private:
114}; 186};
115 187
116GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 188GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
117 : QWidget(parent), child(nullptr), emu_thread(emu_thread) { 189 : QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) {
118 190
119 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 191 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
120 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); 192 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
@@ -137,19 +209,19 @@ void GRenderWindow::moveContext() {
137 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) 209 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
138 ? emu_thread 210 ? emu_thread
139 : qApp->thread(); 211 : qApp->thread();
140 child->context()->moveToThread(thread); 212 context->moveToThread(thread);
141} 213}
142 214
143void GRenderWindow::SwapBuffers() { 215void GRenderWindow::SwapBuffers() {
144 // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`, 216 // In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`,
145 // since we never call `doneCurrent` in this thread. 217 // since we never call `doneCurrent` in this thread.
146 // However: 218 // However:
147 // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called 219 // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
148 // since the last time `swapBuffers` was executed; 220 // since the last time `swapBuffers` was executed;
149 // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. 221 // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks.
150 child->makeCurrent(); 222 context->makeCurrent(child);
151 223
152 child->swapBuffers(); 224 context->swapBuffers(child);
153 if (!first_frame) { 225 if (!first_frame) {
154 emit FirstFrameDisplayed(); 226 emit FirstFrameDisplayed();
155 first_frame = true; 227 first_frame = true;
@@ -157,11 +229,11 @@ void GRenderWindow::SwapBuffers() {
157} 229}
158 230
159void GRenderWindow::MakeCurrent() { 231void GRenderWindow::MakeCurrent() {
160 child->makeCurrent(); 232 context->makeCurrent(child);
161} 233}
162 234
163void GRenderWindow::DoneCurrent() { 235void GRenderWindow::DoneCurrent() {
164 child->doneCurrent(); 236 context->doneCurrent();
165} 237}
166 238
167void GRenderWindow::PollEvents() {} 239void GRenderWindow::PollEvents() {}
@@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {}
174void GRenderWindow::OnFramebufferSizeChanged() { 246void GRenderWindow::OnFramebufferSizeChanged() {
175 // Screen changes potentially incur a change in screen DPI, hence we should update the 247 // Screen changes potentially incur a change in screen DPI, hence we should update the
176 // framebuffer size 248 // framebuffer size
177 qreal pixelRatio = windowPixelRatio(); 249 qreal pixelRatio = GetWindowPixelRatio();
178 unsigned width = child->QPaintDevice::width() * pixelRatio; 250 unsigned width = child->QPaintDevice::width() * pixelRatio;
179 unsigned height = child->QPaintDevice::height() * pixelRatio; 251 unsigned height = child->QPaintDevice::height() * pixelRatio;
180 UpdateCurrentFramebufferLayout(width, height); 252 UpdateCurrentFramebufferLayout(width, height);
181} 253}
182 254
255void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
256 if (child) {
257 child->keyPressEvent(event);
258 }
259}
260
261void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
262 if (child) {
263 child->keyReleaseEvent(event);
264 }
265}
266
183void GRenderWindow::BackupGeometry() { 267void GRenderWindow::BackupGeometry() {
184 geometry = ((QGLWidget*)this)->saveGeometry(); 268 geometry = ((QWidget*)this)->saveGeometry();
185} 269}
186 270
187void GRenderWindow::RestoreGeometry() { 271void GRenderWindow::RestoreGeometry() {
@@ -199,18 +283,18 @@ QByteArray GRenderWindow::saveGeometry() {
199 // If we are a top-level widget, store the current geometry 283 // If we are a top-level widget, store the current geometry
200 // otherwise, store the last backup 284 // otherwise, store the last backup
201 if (parent() == nullptr) 285 if (parent() == nullptr)
202 return ((QGLWidget*)this)->saveGeometry(); 286 return ((QWidget*)this)->saveGeometry();
203 else 287 else
204 return geometry; 288 return geometry;
205} 289}
206 290
207qreal GRenderWindow::windowPixelRatio() const { 291qreal GRenderWindow::GetWindowPixelRatio() const {
208 // windowHandle() might not be accessible until the window is displayed to screen. 292 // windowHandle() might not be accessible until the window is displayed to screen.
209 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; 293 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
210} 294}
211 295
212std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { 296std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
213 const qreal pixel_ratio = windowPixelRatio(); 297 const qreal pixel_ratio = GetWindowPixelRatio();
214 return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), 298 return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
215 static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; 299 static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
216} 300}
@@ -220,47 +304,6 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
220 QWidget::closeEvent(event); 304 QWidget::closeEvent(event);
221} 305}
222 306
223void GRenderWindow::keyPressEvent(QKeyEvent* event) {
224 InputCommon::GetKeyboard()->PressKey(event->key());
225}
226
227void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
228 InputCommon::GetKeyboard()->ReleaseKey(event->key());
229}
230
231void GRenderWindow::mousePressEvent(QMouseEvent* event) {
232 if (event->source() == Qt::MouseEventSynthesizedBySystem)
233 return; // touch input is handled in TouchBeginEvent
234
235 auto pos = event->pos();
236 if (event->button() == Qt::LeftButton) {
237 const auto [x, y] = ScaleTouch(pos);
238 this->TouchPressed(x, y);
239 } else if (event->button() == Qt::RightButton) {
240 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
241 }
242}
243
244void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
245 if (event->source() == Qt::MouseEventSynthesizedBySystem)
246 return; // touch input is handled in TouchUpdateEvent
247
248 auto pos = event->pos();
249 const auto [x, y] = ScaleTouch(pos);
250 this->TouchMoved(x, y);
251 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
252}
253
254void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
255 if (event->source() == Qt::MouseEventSynthesizedBySystem)
256 return; // touch input is handled in TouchEndEvent
257
258 if (event->button() == Qt::LeftButton)
259 this->TouchReleased();
260 else if (event->button() == Qt::RightButton)
261 InputCommon::GetMotionEmu()->EndTilt();
262}
263
264void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { 307void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
265 // TouchBegin always has exactly one touch point, so take the .first() 308 // TouchBegin always has exactly one touch point, so take the .first()
266 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); 309 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
@@ -313,35 +356,60 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
313 NotifyClientAreaSizeChanged(std::make_pair(width, height)); 356 NotifyClientAreaSizeChanged(std::make_pair(width, height));
314} 357}
315 358
359std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
360 return std::make_unique<GGLContext>(shared_context.get());
361}
362
316void GRenderWindow::InitRenderTarget() { 363void GRenderWindow::InitRenderTarget() {
317 if (child) { 364 shared_context.reset();
318 delete child; 365 context.reset();
319 }
320 366
321 if (layout()) { 367 delete child;
322 delete layout(); 368 child = nullptr;
323 } 369
370 delete container;
371 container = nullptr;
372
373 delete layout();
324 374
325 first_frame = false; 375 first_frame = false;
326 376
327 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, 377 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
328 // WA_DontShowOnScreen, WA_DeleteOnClose 378 // WA_DontShowOnScreen, WA_DeleteOnClose
329 QGLFormat fmt; 379 QSurfaceFormat fmt;
330 fmt.setVersion(4, 3); 380 fmt.setVersion(4, 3);
331 fmt.setProfile(QGLFormat::CoreProfile); 381 fmt.setProfile(QSurfaceFormat::CoreProfile);
382 // TODO: expose a setting for buffer value (ie default/single/double/triple)
383 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
384 shared_context = std::make_unique<QOpenGLContext>();
385 shared_context->setFormat(fmt);
386 shared_context->create();
387 context = std::make_unique<QOpenGLContext>();
388 context->setShareContext(shared_context.get());
389 context->setFormat(fmt);
390 context->create();
332 fmt.setSwapInterval(false); 391 fmt.setSwapInterval(false);
333 392
334 // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X 393 child = new GGLWidgetInternal(this, shared_context.get());
335 fmt.setOption(QGL::NoDeprecatedFunctions); 394 container = QWidget::createWindowContainer(child, this);
336 395
337 child = new GGLWidgetInternal(fmt, this);
338 QBoxLayout* layout = new QHBoxLayout(this); 396 QBoxLayout* layout = new QHBoxLayout(this);
339 397 layout->addWidget(container);
340 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
341 layout->addWidget(child);
342 layout->setMargin(0); 398 layout->setMargin(0);
343 setLayout(layout); 399 setLayout(layout);
344 400
401 // Reset minimum size to avoid unwanted resizes when this function is called for a second time.
402 setMinimumSize(1, 1);
403
404 // Show causes the window to actually be created and the OpenGL context as well, but we don't
405 // want the widget to be shown yet, so immediately hide it.
406 show();
407 hide();
408
409 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
410 child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
411 container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
412
345 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 413 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
346 414
347 OnFramebufferSizeChanged(); 415 OnFramebufferSizeChanged();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 3183621bc..9608b959f 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -7,9 +7,9 @@
7#include <atomic> 7#include <atomic>
8#include <condition_variable> 8#include <condition_variable>
9#include <mutex> 9#include <mutex>
10#include <QGLWidget>
11#include <QImage> 10#include <QImage>
12#include <QThread> 11#include <QThread>
12#include <QWidget>
13#include "common/thread.h" 13#include "common/thread.h"
14#include "core/core.h" 14#include "core/core.h"
15#include "core/frontend/emu_window.h" 15#include "core/frontend/emu_window.h"
@@ -21,6 +21,8 @@ class QTouchEvent;
21class GGLWidgetInternal; 21class GGLWidgetInternal;
22class GMainWindow; 22class GMainWindow;
23class GRenderWindow; 23class GRenderWindow;
24class QSurface;
25class QOpenGLContext;
24 26
25namespace VideoCore { 27namespace VideoCore {
26enum class LoadCallbackStage; 28enum class LoadCallbackStage;
@@ -121,25 +123,21 @@ public:
121 void MakeCurrent() override; 123 void MakeCurrent() override;
122 void DoneCurrent() override; 124 void DoneCurrent() override;
123 void PollEvents() override; 125 void PollEvents() override;
126 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
127
128 void ForwardKeyPressEvent(QKeyEvent* event);
129 void ForwardKeyReleaseEvent(QKeyEvent* event);
124 130
125 void BackupGeometry(); 131 void BackupGeometry();
126 void RestoreGeometry(); 132 void RestoreGeometry();
127 void restoreGeometry(const QByteArray& geometry); // overridden 133 void restoreGeometry(const QByteArray& geometry); // overridden
128 QByteArray saveGeometry(); // overridden 134 QByteArray saveGeometry(); // overridden
129 135
130 qreal windowPixelRatio() const; 136 qreal GetWindowPixelRatio() const;
137 std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
131 138
132 void closeEvent(QCloseEvent* event) override; 139 void closeEvent(QCloseEvent* event) override;
133
134 void keyPressEvent(QKeyEvent* event) override;
135 void keyReleaseEvent(QKeyEvent* event) override;
136
137 void mousePressEvent(QMouseEvent* event) override;
138 void mouseMoveEvent(QMouseEvent* event) override;
139 void mouseReleaseEvent(QMouseEvent* event) override;
140
141 bool event(QEvent* event) override; 140 bool event(QEvent* event) override;
142
143 void focusOutEvent(QFocusEvent* event) override; 141 void focusOutEvent(QFocusEvent* event) override;
144 142
145 void OnClientAreaResized(unsigned width, unsigned height); 143 void OnClientAreaResized(unsigned width, unsigned height);
@@ -161,7 +159,6 @@ signals:
161 void FirstFrameDisplayed(); 159 void FirstFrameDisplayed();
162 160
163private: 161private:
164 std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
165 void TouchBeginEvent(const QTouchEvent* event); 162 void TouchBeginEvent(const QTouchEvent* event);
166 void TouchUpdateEvent(const QTouchEvent* event); 163 void TouchUpdateEvent(const QTouchEvent* event);
167 void TouchEndEvent(); 164 void TouchEndEvent();
@@ -169,11 +166,17 @@ private:
169 void OnMinimalClientAreaChangeRequest( 166 void OnMinimalClientAreaChangeRequest(
170 const std::pair<unsigned, unsigned>& minimal_size) override; 167 const std::pair<unsigned, unsigned>& minimal_size) override;
171 168
172 GGLWidgetInternal* child; 169 QWidget* container = nullptr;
170 GGLWidgetInternal* child = nullptr;
173 171
174 QByteArray geometry; 172 QByteArray geometry;
175 173
176 EmuThread* emu_thread; 174 EmuThread* emu_thread;
175 // Context that backs the GGLWidgetInternal (and will be used by core to render)
176 std::unique_ptr<QOpenGLContext> context;
177 // Context that will be shared between all newly created contexts. This should never be made
178 // current
179 std::unique_ptr<QOpenGLContext> shared_context;
177 180
178 /// Temporary storage of the screenshot taken 181 /// Temporary storage of the screenshot taken
179 QImage screenshot_image; 182 QImage screenshot_image;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index dead9f807..8725a78dc 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.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 <array>
6#include <QKeySequence>
5#include <QSettings> 7#include <QSettings>
6#include "common/file_util.h" 8#include "common/file_util.h"
7#include "configure_input_simple.h" 9#include "configure_input_simple.h"
@@ -9,7 +11,6 @@
9#include "core/hle/service/hid/controllers/npad.h" 11#include "core/hle/service/hid/controllers/npad.h"
10#include "input_common/main.h" 12#include "input_common/main.h"
11#include "yuzu/configuration/config.h" 13#include "yuzu/configuration/config.h"
12#include "yuzu/ui_settings.h"
13 14
14Config::Config() { 15Config::Config() {
15 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 16 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@@ -17,7 +18,6 @@ Config::Config() {
17 FileUtil::CreateFullPath(qt_config_loc); 18 FileUtil::CreateFullPath(qt_config_loc);
18 qt_config = 19 qt_config =
19 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 20 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
20
21 Reload(); 21 Reload();
22} 22}
23 23
@@ -205,6 +205,27 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
205 Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight, 205 Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
206}; 206};
207 207
208// This shouldn't have anything except static initializers (no functions). So
209// QKeySequnce(...).toString() is NOT ALLOWED HERE.
210// This must be in alphabetical order according to action name as it must have the same order as
211// UISetting::values.shortcuts, which is alphabetically ordered.
212const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{
213 {{"Capture Screenshot", "Main Window", {"Ctrl+P", Qt::ApplicationShortcut}},
214 {"Continue/Pause Emulation", "Main Window", {"F4", Qt::WindowShortcut}},
215 {"Decrease Speed Limit", "Main Window", {"-", Qt::ApplicationShortcut}},
216 {"Exit yuzu", "Main Window", {"Ctrl+Q", Qt::WindowShortcut}},
217 {"Exit Fullscreen", "Main Window", {"Esc", Qt::WindowShortcut}},
218 {"Fullscreen", "Main Window", {"F11", Qt::WindowShortcut}},
219 {"Increase Speed Limit", "Main Window", {"+", Qt::ApplicationShortcut}},
220 {"Load Amiibo", "Main Window", {"F2", Qt::ApplicationShortcut}},
221 {"Load File", "Main Window", {"Ctrl+O", Qt::WindowShortcut}},
222 {"Restart Emulation", "Main Window", {"F6", Qt::WindowShortcut}},
223 {"Stop Emulation", "Main Window", {"F5", Qt::WindowShortcut}},
224 {"Toggle Filter Bar", "Main Window", {"Ctrl+F", Qt::WindowShortcut}},
225 {"Toggle Speed Limit", "Main Window", {"Ctrl+Z", Qt::ApplicationShortcut}},
226 {"Toggle Status Bar", "Main Window", {"Ctrl+S", Qt::WindowShortcut}},
227 {"Change Docked Mode", "Main Window", {"F10", Qt::ApplicationShortcut}}}};
228
208void Config::ReadPlayerValues() { 229void Config::ReadPlayerValues() {
209 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 230 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
210 auto& player = Settings::values.players[p]; 231 auto& player = Settings::values.players[p];
@@ -373,6 +394,7 @@ void Config::ReadValues() {
373 ReadSetting("use_accurate_gpu_emulation", false).toBool(); 394 ReadSetting("use_accurate_gpu_emulation", false).toBool();
374 Settings::values.use_asynchronous_gpu_emulation = 395 Settings::values.use_asynchronous_gpu_emulation =
375 ReadSetting("use_asynchronous_gpu_emulation", false).toBool(); 396 ReadSetting("use_asynchronous_gpu_emulation", false).toBool();
397 Settings::values.force_30fps_mode = ReadSetting("force_30fps_mode", false).toBool();
376 398
377 Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); 399 Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat();
378 Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); 400 Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat();
@@ -502,26 +524,21 @@ void Config::ReadValues() {
502 qt_config->beginGroup("Paths"); 524 qt_config->beginGroup("Paths");
503 UISettings::values.roms_path = ReadSetting("romsPath").toString(); 525 UISettings::values.roms_path = ReadSetting("romsPath").toString();
504 UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); 526 UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
505 UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString(); 527 UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString();
506 UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool(); 528 UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool();
507 UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); 529 UISettings::values.recent_files = ReadSetting("recentFiles").toStringList();
508 qt_config->endGroup(); 530 qt_config->endGroup();
509 531
510 qt_config->beginGroup("Shortcuts"); 532 qt_config->beginGroup("Shortcuts");
511 QStringList groups = qt_config->childGroups(); 533 for (auto [name, group, shortcut] : default_hotkeys) {
512 for (auto group : groups) { 534 auto [keyseq, context] = shortcut;
513 qt_config->beginGroup(group); 535 qt_config->beginGroup(group);
514 536 qt_config->beginGroup(name);
515 QStringList hotkeys = qt_config->childGroups(); 537 UISettings::values.shortcuts.push_back(
516 for (auto hotkey : hotkeys) { 538 {name,
517 qt_config->beginGroup(hotkey); 539 group,
518 UISettings::values.shortcuts.emplace_back(UISettings::Shortcut( 540 {ReadSetting("KeySeq", keyseq).toString(), ReadSetting("Context", context).toInt()}});
519 group + "/" + hotkey, 541 qt_config->endGroup();
520 UISettings::ContextualShortcut(ReadSetting("KeySeq").toString(),
521 ReadSetting("Context").toInt())));
522 qt_config->endGroup();
523 }
524
525 qt_config->endGroup(); 542 qt_config->endGroup();
526 } 543 }
527 qt_config->endGroup(); 544 qt_config->endGroup();
@@ -648,6 +665,7 @@ void Config::SaveValues() {
648 WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); 665 WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false);
649 WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, 666 WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation,
650 false); 667 false);
668 WriteSetting("force_30fps_mode", Settings::values.force_30fps_mode, false);
651 669
652 // Cast to double because Qt's written float values are not human-readable 670 // Cast to double because Qt's written float values are not human-readable
653 WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0); 671 WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0);
@@ -752,15 +770,22 @@ void Config::SaveValues() {
752 WriteSetting("romsPath", UISettings::values.roms_path); 770 WriteSetting("romsPath", UISettings::values.roms_path);
753 WriteSetting("symbolsPath", UISettings::values.symbols_path); 771 WriteSetting("symbolsPath", UISettings::values.symbols_path);
754 WriteSetting("screenshotPath", UISettings::values.screenshot_path); 772 WriteSetting("screenshotPath", UISettings::values.screenshot_path);
755 WriteSetting("gameListRootDir", UISettings::values.gamedir, "."); 773 WriteSetting("gameListRootDir", UISettings::values.game_directory_path, ".");
756 WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false); 774 WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false);
757 WriteSetting("recentFiles", UISettings::values.recent_files); 775 WriteSetting("recentFiles", UISettings::values.recent_files);
758 qt_config->endGroup(); 776 qt_config->endGroup();
759 777
760 qt_config->beginGroup("Shortcuts"); 778 qt_config->beginGroup("Shortcuts");
761 for (auto shortcut : UISettings::values.shortcuts) { 779 // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
762 WriteSetting(shortcut.first + "/KeySeq", shortcut.second.first); 780 // However, their ordering must also be the same.
763 WriteSetting(shortcut.first + "/Context", shortcut.second.second); 781 for (std::size_t i = 0; i < default_hotkeys.size(); i++) {
782 auto [name, group, shortcut] = UISettings::values.shortcuts[i];
783 qt_config->beginGroup(group);
784 qt_config->beginGroup(name);
785 WriteSetting("KeySeq", shortcut.first, default_hotkeys[i].shortcut.first);
786 WriteSetting("Context", shortcut.second, default_hotkeys[i].shortcut.second);
787 qt_config->endGroup();
788 qt_config->endGroup();
764 } 789 }
765 qt_config->endGroup(); 790 qt_config->endGroup();
766 791
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index f4185db18..221d2364c 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -9,6 +9,7 @@
9#include <string> 9#include <string>
10#include <QVariant> 10#include <QVariant>
11#include "core/settings.h" 11#include "core/settings.h"
12#include "yuzu/ui_settings.h"
12 13
13class QSettings; 14class QSettings;
14 15
@@ -47,6 +48,8 @@ private:
47 void WriteSetting(const QString& name, const QVariant& value); 48 void WriteSetting(const QString& name, const QVariant& value);
48 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); 49 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
49 50
51 static const std::array<UISettings::Shortcut, 15> default_hotkeys;
52
50 std::unique_ptr<QSettings> qt_config; 53 std::unique_ptr<QSettings> qt_config;
51 std::string qt_config_loc; 54 std::string qt_config_loc;
52}; 55};
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 3f03f0b77..267717bc9 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -7,9 +7,15 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>382</width> 9 <width>382</width>
10 <height>241</height> 10 <height>650</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="minimumSize">
14 <size>
15 <width>0</width>
16 <height>650</height>
17 </size>
18 </property>
13 <property name="windowTitle"> 19 <property name="windowTitle">
14 <string>yuzu Configuration</string> 20 <string>yuzu Configuration</string>
15 </property> 21 </property>
@@ -62,6 +68,11 @@
62 <string>Input</string> 68 <string>Input</string>
63 </attribute> 69 </attribute>
64 </widget> 70 </widget>
71 <widget class="ConfigureHotkeys" name="hotkeysTab">
72 <attribute name="title">
73 <string>Hotkeys</string>
74 </attribute>
75 </widget>
65 <widget class="ConfigureGraphics" name="graphicsTab"> 76 <widget class="ConfigureGraphics" name="graphicsTab">
66 <attribute name="title"> 77 <attribute name="title">
67 <string>Graphics</string> 78 <string>Graphics</string>
@@ -150,6 +161,12 @@
150 <header>configuration/configure_input_simple.h</header> 161 <header>configuration/configure_input_simple.h</header>
151 <container>1</container> 162 <container>1</container>
152 </customwidget> 163 </customwidget>
164 <customwidget>
165 <class>ConfigureHotkeys</class>
166 <extends>QWidget</extends>
167 <header>configuration/configure_hotkeys.h</header>
168 <container>1</container>
169 </customwidget>
153 </customwidgets> 170 </customwidgets>
154 <resources/> 171 <resources/>
155 <connections> 172 <connections>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 777050405..a5218b051 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -8,20 +8,22 @@
8#include "ui_configure.h" 8#include "ui_configure.h"
9#include "yuzu/configuration/config.h" 9#include "yuzu/configuration/config.h"
10#include "yuzu/configuration/configure_dialog.h" 10#include "yuzu/configuration/configure_dialog.h"
11#include "yuzu/configuration/configure_input_player.h"
11#include "yuzu/hotkeys.h" 12#include "yuzu/hotkeys.h"
12 13
13ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry) 14ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
14 : QDialog(parent), ui(new Ui::ConfigureDialog) { 15 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
15 ui->setupUi(this); 16 ui->setupUi(this);
16 ui->generalTab->PopulateHotkeyList(registry); 17 ui->hotkeysTab->Populate(registry);
17 this->setConfiguration(); 18 this->setConfiguration();
18 this->PopulateSelectionList(); 19 this->PopulateSelectionList();
19 connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, 20 connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
20 &ConfigureDialog::UpdateVisibleTabs); 21 &ConfigureDialog::UpdateVisibleTabs);
21
22 adjustSize(); 22 adjustSize();
23
24 ui->selectorList->setCurrentRow(0); 23 ui->selectorList->setCurrentRow(0);
24
25 // Synchronise lists upon initialisation
26 ui->hotkeysTab->EmitHotkeysChanged();
25} 27}
26 28
27ConfigureDialog::~ConfigureDialog() = default; 29ConfigureDialog::~ConfigureDialog() = default;
@@ -34,6 +36,7 @@ void ConfigureDialog::applyConfiguration() {
34 ui->systemTab->applyConfiguration(); 36 ui->systemTab->applyConfiguration();
35 ui->profileManagerTab->applyConfiguration(); 37 ui->profileManagerTab->applyConfiguration();
36 ui->inputTab->applyConfiguration(); 38 ui->inputTab->applyConfiguration();
39 ui->hotkeysTab->applyConfiguration(registry);
37 ui->graphicsTab->applyConfiguration(); 40 ui->graphicsTab->applyConfiguration();
38 ui->audioTab->applyConfiguration(); 41 ui->audioTab->applyConfiguration();
39 ui->debugTab->applyConfiguration(); 42 ui->debugTab->applyConfiguration();
@@ -47,7 +50,7 @@ void ConfigureDialog::PopulateSelectionList() {
47 {{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}}, 50 {{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}},
48 {tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}}, 51 {tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}},
49 {tr("Graphics"), {tr("Graphics")}}, 52 {tr("Graphics"), {tr("Graphics")}},
50 {tr("Controls"), {tr("Input")}}}}; 53 {tr("Controls"), {tr("Input"), tr("Hotkeys")}}}};
51 54
52 for (const auto& entry : items) { 55 for (const auto& entry : items) {
53 auto* const item = new QListWidgetItem(entry.first); 56 auto* const item = new QListWidgetItem(entry.first);
@@ -66,6 +69,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
66 {tr("System"), ui->systemTab}, 69 {tr("System"), ui->systemTab},
67 {tr("Profiles"), ui->profileManagerTab}, 70 {tr("Profiles"), ui->profileManagerTab},
68 {tr("Input"), ui->inputTab}, 71 {tr("Input"), ui->inputTab},
72 {tr("Hotkeys"), ui->hotkeysTab},
69 {tr("Graphics"), ui->graphicsTab}, 73 {tr("Graphics"), ui->graphicsTab},
70 {tr("Audio"), ui->audioTab}, 74 {tr("Audio"), ui->audioTab},
71 {tr("Debug"), ui->debugTab}, 75 {tr("Debug"), ui->debugTab},
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 243d9fa09..2363ba584 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -17,7 +17,7 @@ class ConfigureDialog : public QDialog {
17 Q_OBJECT 17 Q_OBJECT
18 18
19public: 19public:
20 explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry); 20 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry);
21 ~ConfigureDialog() override; 21 ~ConfigureDialog() override;
22 22
23 void applyConfiguration(); 23 void applyConfiguration();
@@ -28,4 +28,5 @@ private:
28 void PopulateSelectionList(); 28 void PopulateSelectionList();
29 29
30 std::unique_ptr<Ui::ConfigureDialog> ui; 30 std::unique_ptr<Ui::ConfigureDialog> ui;
31 HotkeyRegistry& registry;
31}; 32};
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 389fcf667..e48f4f5a3 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -28,19 +28,15 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
28ConfigureGeneral::~ConfigureGeneral() = default; 28ConfigureGeneral::~ConfigureGeneral() = default;
29 29
30void ConfigureGeneral::setConfiguration() { 30void ConfigureGeneral::setConfiguration() {
31 ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); 31 ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan);
32 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); 32 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
33 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); 33 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
34 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 34 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
35 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); 35 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
36} 36}
37 37
38void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
39 ui->widget->Populate(registry);
40}
41
42void ConfigureGeneral::applyConfiguration() { 38void ConfigureGeneral::applyConfiguration() {
43 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); 39 UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked();
44 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 40 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
45 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); 41 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
46 UISettings::values.theme = 42 UISettings::values.theme =
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 59738af40..df41d995b 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -20,7 +20,6 @@ public:
20 explicit ConfigureGeneral(QWidget* parent = nullptr); 20 explicit ConfigureGeneral(QWidget* parent = nullptr);
21 ~ConfigureGeneral() override; 21 ~ConfigureGeneral() override;
22 22
23 void PopulateHotkeyList(const HotkeyRegistry& registry);
24 void applyConfiguration(); 23 void applyConfiguration();
25 24
26private: 25private:
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 01d1c0b8e..1a5721fe7 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -98,22 +98,6 @@
98 </widget> 98 </widget>
99 </item> 99 </item>
100 <item> 100 <item>
101 <widget class="QGroupBox" name="HotKeysGroupBox">
102 <property name="title">
103 <string>Hotkeys</string>
104 </property>
105 <layout class="QHBoxLayout" name="HotKeysHorizontalLayout">
106 <item>
107 <layout class="QVBoxLayout" name="HotKeysVerticalLayout">
108 <item>
109 <widget class="GHotkeysDialog" name="widget" native="true"/>
110 </item>
111 </layout>
112 </item>
113 </layout>
114 </widget>
115 </item>
116 <item>
117 <spacer name="verticalSpacer"> 101 <spacer name="verticalSpacer">
118 <property name="orientation"> 102 <property name="orientation">
119 <enum>Qt::Vertical</enum> 103 <enum>Qt::Vertical</enum>
@@ -130,14 +114,6 @@
130 </item> 114 </item>
131 </layout> 115 </layout>
132 </widget> 116 </widget>
133 <customwidgets>
134 <customwidget>
135 <class>GHotkeysDialog</class>
136 <extends>QWidget</extends>
137 <header>hotkeys.h</header>
138 <container>1</container>
139 </customwidget>
140 </customwidgets>
141 <resources/> 117 <resources/>
142 <connections/> 118 <connections/>
143</ui> 119</ui>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index dd1d67488..0a9883d37 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -77,6 +77,8 @@ void ConfigureGraphics::setConfiguration() {
77 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); 77 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
78 ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 78 ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn());
79 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); 79 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
80 ui->force_30fps_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
81 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
80 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 82 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
81 Settings::values.bg_blue)); 83 Settings::values.bg_blue));
82} 84}
@@ -90,6 +92,7 @@ void ConfigureGraphics::applyConfiguration() {
90 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); 92 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
91 Settings::values.use_asynchronous_gpu_emulation = 93 Settings::values.use_asynchronous_gpu_emulation =
92 ui->use_asynchronous_gpu_emulation->isChecked(); 94 ui->use_asynchronous_gpu_emulation->isChecked();
95 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
93 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 96 Settings::values.bg_red = static_cast<float>(bg_color.redF());
94 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 97 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
95 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 98 Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index c6767e0ca..15ab18ecd 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -71,6 +71,13 @@
71 </widget> 71 </widget>
72 </item> 72 </item>
73 <item> 73 <item>
74 <widget class="QCheckBox" name="force_30fps_mode">
75 <property name="text">
76 <string>Force 30 FPS mode</string>
77 </property>
78 </widget>
79 </item>
80 <item>
74 <layout class="QHBoxLayout" name="horizontalLayout"> 81 <layout class="QHBoxLayout" name="horizontalLayout">
75 <item> 82 <item>
76 <widget class="QLabel" name="label"> 83 <widget class="QLabel" name="label">
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
new file mode 100644
index 000000000..a7a8752e5
--- /dev/null
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -0,0 +1,122 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QMessageBox>
6#include <QStandardItemModel>
7#include "core/settings.h"
8#include "ui_configure_hotkeys.h"
9#include "yuzu/configuration/configure_hotkeys.h"
10#include "yuzu/hotkeys.h"
11#include "yuzu/util/sequence_dialog/sequence_dialog.h"
12
13ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
14 : QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()) {
15 ui->setupUi(this);
16 setFocusPolicy(Qt::ClickFocus);
17
18 model = new QStandardItemModel(this);
19 model->setColumnCount(3);
20 model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")});
21
22 connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure);
23 ui->hotkey_list->setModel(model);
24
25 // TODO(Kloen): Make context configurable as well (hiding the column for now)
26 ui->hotkey_list->hideColumn(2);
27
28 ui->hotkey_list->setColumnWidth(0, 200);
29 ui->hotkey_list->resizeColumnToContents(1);
30}
31
32ConfigureHotkeys::~ConfigureHotkeys() = default;
33
34void ConfigureHotkeys::EmitHotkeysChanged() {
35 emit HotkeysChanged(GetUsedKeyList());
36}
37
38QList<QKeySequence> ConfigureHotkeys::GetUsedKeyList() const {
39 QList<QKeySequence> list;
40 for (int r = 0; r < model->rowCount(); r++) {
41 const QStandardItem* parent = model->item(r, 0);
42 for (int r2 = 0; r2 < parent->rowCount(); r2++) {
43 const QStandardItem* keyseq = parent->child(r2, 1);
44 list << QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText);
45 }
46 }
47 return list;
48}
49
50void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
51 for (const auto& group : registry.hotkey_groups) {
52 auto* parent_item = new QStandardItem(group.first);
53 parent_item->setEditable(false);
54 for (const auto& hotkey : group.second) {
55 auto* action = new QStandardItem(hotkey.first);
56 auto* keyseq =
57 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
58 action->setEditable(false);
59 keyseq->setEditable(false);
60 parent_item->appendRow({action, keyseq});
61 }
62 model->appendRow(parent_item);
63 }
64
65 ui->hotkey_list->expandAll();
66}
67
68void ConfigureHotkeys::Configure(QModelIndex index) {
69 if (!index.parent().isValid()) {
70 return;
71 }
72
73 index = index.sibling(index.row(), 1);
74 auto* const model = ui->hotkey_list->model();
75 const auto previous_key = model->data(index);
76
77 SequenceDialog hotkey_dialog{this};
78
79 const int return_code = hotkey_dialog.exec();
80 const auto key_sequence = hotkey_dialog.GetSequence();
81 if (return_code == QDialog::Rejected || key_sequence.isEmpty()) {
82 return;
83 }
84
85 if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) {
86 QMessageBox::critical(this, tr("Error in inputted key"),
87 tr("You're using a key that's already bound."));
88 } else {
89 model->setData(index, key_sequence.toString(QKeySequence::NativeText));
90 EmitHotkeysChanged();
91 }
92}
93
94bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const {
95 return GetUsedKeyList().contains(key_sequence);
96}
97
98void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) {
99 for (int key_id = 0; key_id < model->rowCount(); key_id++) {
100 const QStandardItem* parent = model->item(key_id, 0);
101 for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
102 const QStandardItem* action = parent->child(key_column_id, 0);
103 const QStandardItem* keyseq = parent->child(key_column_id, 1);
104 for (auto& [group, sub_actions] : registry.hotkey_groups) {
105 if (group != parent->text())
106 continue;
107 for (auto& [action_name, hotkey] : sub_actions) {
108 if (action_name != action->text())
109 continue;
110 hotkey.keyseq = QKeySequence(keyseq->text());
111 }
112 }
113 }
114 }
115
116 registry.SaveHotkeys();
117 Settings::Apply();
118}
119
120void ConfigureHotkeys::retranslateUi() {
121 ui->retranslateUi(this);
122}
diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h
new file mode 100644
index 000000000..73fb8a175
--- /dev/null
+++ b/src/yuzu/configuration/configure_hotkeys.h
@@ -0,0 +1,47 @@
1// Copyright 2017 Citra 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 <memory>
8#include <QWidget>
9
10namespace Ui {
11class ConfigureHotkeys;
12}
13
14class HotkeyRegistry;
15class QStandardItemModel;
16
17class ConfigureHotkeys : public QWidget {
18 Q_OBJECT
19
20public:
21 explicit ConfigureHotkeys(QWidget* parent = nullptr);
22 ~ConfigureHotkeys() override;
23
24 void applyConfiguration(HotkeyRegistry& registry);
25 void retranslateUi();
26
27 void EmitHotkeysChanged();
28
29 /**
30 * Populates the hotkey list widget using data from the provided registry.
31 * Called everytime the Configure dialog is opened.
32 * @param registry The HotkeyRegistry whose data is used to populate the list.
33 */
34 void Populate(const HotkeyRegistry& registry);
35
36signals:
37 void HotkeysChanged(QList<QKeySequence> new_key_list);
38
39private:
40 void Configure(QModelIndex index);
41 bool IsUsedKey(QKeySequence key_sequence) const;
42 QList<QKeySequence> GetUsedKeyList() const;
43
44 std::unique_ptr<Ui::ConfigureHotkeys> ui;
45
46 QStandardItemModel* model;
47};
diff --git a/src/yuzu/configuration/configure_hotkeys.ui b/src/yuzu/configuration/configure_hotkeys.ui
new file mode 100644
index 000000000..0d0b70f38
--- /dev/null
+++ b/src/yuzu/configuration/configure_hotkeys.ui
@@ -0,0 +1,42 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureHotkeys</class>
4 <widget class="QWidget" name="ConfigureHotkeys">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>363</width>
10 <height>388</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Hotkey Settings</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_2">
19 <item>
20 <widget class="QLabel" name="label_2">
21 <property name="text">
22 <string>Double-click on a binding to change it.</string>
23 </property>
24 </widget>
25 </item>
26 <item>
27 <widget class="QTreeView" name="hotkey_list">
28 <property name="editTriggers">
29 <set>QAbstractItemView::NoEditTriggers</set>
30 </property>
31 <property name="sortingEnabled">
32 <bool>false</bool>
33 </property>
34 </widget>
35 </item>
36 </layout>
37 </item>
38 </layout>
39 </widget>
40 <resources/>
41 <connections/>
42</ui> \ No newline at end of file
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
deleted file mode 100644
index 11023ed63..000000000
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ /dev/null
@@ -1,460 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QBoxLayout>
6#include <QComboBox>
7#include <QDebug>
8#include <QFileDialog>
9#include <QLabel>
10#include <QMouseEvent>
11#include <QPushButton>
12#include <QScrollArea>
13#include <QSpinBox>
14#include "common/vector_math.h"
15#include "core/core.h"
16#include "core/memory.h"
17#include "video_core/engines/maxwell_3d.h"
18#include "video_core/gpu.h"
19#include "video_core/textures/decoders.h"
20#include "video_core/textures/texture.h"
21#include "yuzu/debugger/graphics/graphics_surface.h"
22#include "yuzu/util/spinbox.h"
23
24static Tegra::Texture::TextureFormat ConvertToTextureFormat(
25 Tegra::RenderTargetFormat render_target_format) {
26 switch (render_target_format) {
27 case Tegra::RenderTargetFormat::RGBA8_UNORM:
28 return Tegra::Texture::TextureFormat::A8R8G8B8;
29 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
30 return Tegra::Texture::TextureFormat::A2B10G10R10;
31 default:
32 UNIMPLEMENTED_MSG("Unimplemented RT format");
33 return Tegra::Texture::TextureFormat::A8R8G8B8;
34 }
35}
36
37SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
38 : QLabel(parent), surface_widget(surface_widget_) {}
39
40SurfacePicture::~SurfacePicture() = default;
41
42void SurfacePicture::mousePressEvent(QMouseEvent* event) {
43 // Only do something while the left mouse button is held down
44 if (!(event->buttons() & Qt::LeftButton))
45 return;
46
47 if (pixmap() == nullptr)
48 return;
49
50 if (surface_widget)
51 surface_widget->Pick(event->x() * pixmap()->width() / width(),
52 event->y() * pixmap()->height() / height());
53}
54
55void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
56 // We also want to handle the event if the user moves the mouse while holding down the LMB
57 mousePressEvent(event);
58}
59
60GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
61 QWidget* parent)
62 : BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent),
63 surface_source(Source::RenderTarget0) {
64 setObjectName("MaxwellSurface");
65
66 surface_source_list = new QComboBox;
67 surface_source_list->addItem(tr("Render Target 0"));
68 surface_source_list->addItem(tr("Render Target 1"));
69 surface_source_list->addItem(tr("Render Target 2"));
70 surface_source_list->addItem(tr("Render Target 3"));
71 surface_source_list->addItem(tr("Render Target 4"));
72 surface_source_list->addItem(tr("Render Target 5"));
73 surface_source_list->addItem(tr("Render Target 6"));
74 surface_source_list->addItem(tr("Render Target 7"));
75 surface_source_list->addItem(tr("Z Buffer"));
76 surface_source_list->addItem(tr("Custom"));
77 surface_source_list->setCurrentIndex(static_cast<int>(surface_source));
78
79 surface_address_control = new CSpinBox;
80 surface_address_control->SetBase(16);
81 surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF);
82 surface_address_control->SetPrefix("0x");
83
84 unsigned max_dimension = 16384; // TODO: Find actual maximum
85
86 surface_width_control = new QSpinBox;
87 surface_width_control->setRange(0, max_dimension);
88
89 surface_height_control = new QSpinBox;
90 surface_height_control->setRange(0, max_dimension);
91
92 surface_picker_x_control = new QSpinBox;
93 surface_picker_x_control->setRange(0, max_dimension - 1);
94
95 surface_picker_y_control = new QSpinBox;
96 surface_picker_y_control->setRange(0, max_dimension - 1);
97
98 surface_format_control = new QComboBox;
99
100 // Color formats sorted by Maxwell texture format index
101 surface_format_control->addItem(tr("None"));
102 surface_format_control->addItem(tr("Unknown"));
103 surface_format_control->addItem(tr("Unknown"));
104 surface_format_control->addItem(tr("Unknown"));
105 surface_format_control->addItem(tr("Unknown"));
106 surface_format_control->addItem(tr("Unknown"));
107 surface_format_control->addItem(tr("Unknown"));
108 surface_format_control->addItem(tr("Unknown"));
109 surface_format_control->addItem(tr("A8R8G8B8"));
110 surface_format_control->addItem(tr("Unknown"));
111 surface_format_control->addItem(tr("Unknown"));
112 surface_format_control->addItem(tr("Unknown"));
113 surface_format_control->addItem(tr("Unknown"));
114 surface_format_control->addItem(tr("Unknown"));
115 surface_format_control->addItem(tr("Unknown"));
116 surface_format_control->addItem(tr("Unknown"));
117 surface_format_control->addItem(tr("Unknown"));
118 surface_format_control->addItem(tr("Unknown"));
119 surface_format_control->addItem(tr("Unknown"));
120 surface_format_control->addItem(tr("Unknown"));
121 surface_format_control->addItem(tr("Unknown"));
122 surface_format_control->addItem(tr("Unknown"));
123 surface_format_control->addItem(tr("Unknown"));
124 surface_format_control->addItem(tr("Unknown"));
125 surface_format_control->addItem(tr("Unknown"));
126 surface_format_control->addItem(tr("Unknown"));
127 surface_format_control->addItem(tr("Unknown"));
128 surface_format_control->addItem(tr("Unknown"));
129 surface_format_control->addItem(tr("Unknown"));
130 surface_format_control->addItem(tr("Unknown"));
131 surface_format_control->addItem(tr("Unknown"));
132 surface_format_control->addItem(tr("Unknown"));
133 surface_format_control->addItem(tr("Unknown"));
134 surface_format_control->addItem(tr("Unknown"));
135 surface_format_control->addItem(tr("Unknown"));
136 surface_format_control->addItem(tr("Unknown"));
137 surface_format_control->addItem(tr("DXT1"));
138 surface_format_control->addItem(tr("DXT23"));
139 surface_format_control->addItem(tr("DXT45"));
140 surface_format_control->addItem(tr("DXN1"));
141 surface_format_control->addItem(tr("DXN2"));
142
143 surface_info_label = new QLabel();
144 surface_info_label->setWordWrap(true);
145
146 surface_picture_label = new SurfacePicture(0, this);
147 surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
148 surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
149 surface_picture_label->setScaledContents(false);
150
151 auto scroll_area = new QScrollArea();
152 scroll_area->setBackgroundRole(QPalette::Dark);
153 scroll_area->setWidgetResizable(false);
154 scroll_area->setWidget(surface_picture_label);
155
156 save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save"));
157
158 // Connections
159 connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate);
160 connect(surface_source_list,
161 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
162 &GraphicsSurfaceWidget::OnSurfaceSourceChanged);
163 connect(surface_address_control, &CSpinBox::ValueChanged, this,
164 &GraphicsSurfaceWidget::OnSurfaceAddressChanged);
165 connect(surface_width_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
166 this, &GraphicsSurfaceWidget::OnSurfaceWidthChanged);
167 connect(surface_height_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
168 this, &GraphicsSurfaceWidget::OnSurfaceHeightChanged);
169 connect(surface_format_control,
170 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
171 &GraphicsSurfaceWidget::OnSurfaceFormatChanged);
172 connect(surface_picker_x_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
173 this, &GraphicsSurfaceWidget::OnSurfacePickerXChanged);
174 connect(surface_picker_y_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
175 this, &GraphicsSurfaceWidget::OnSurfacePickerYChanged);
176 connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface);
177
178 auto main_widget = new QWidget;
179 auto main_layout = new QVBoxLayout;
180 {
181 auto sub_layout = new QHBoxLayout;
182 sub_layout->addWidget(new QLabel(tr("Source:")));
183 sub_layout->addWidget(surface_source_list);
184 main_layout->addLayout(sub_layout);
185 }
186 {
187 auto sub_layout = new QHBoxLayout;
188 sub_layout->addWidget(new QLabel(tr("GPU Address:")));
189 sub_layout->addWidget(surface_address_control);
190 main_layout->addLayout(sub_layout);
191 }
192 {
193 auto sub_layout = new QHBoxLayout;
194 sub_layout->addWidget(new QLabel(tr("Width:")));
195 sub_layout->addWidget(surface_width_control);
196 main_layout->addLayout(sub_layout);
197 }
198 {
199 auto sub_layout = new QHBoxLayout;
200 sub_layout->addWidget(new QLabel(tr("Height:")));
201 sub_layout->addWidget(surface_height_control);
202 main_layout->addLayout(sub_layout);
203 }
204 {
205 auto sub_layout = new QHBoxLayout;
206 sub_layout->addWidget(new QLabel(tr("Format:")));
207 sub_layout->addWidget(surface_format_control);
208 main_layout->addLayout(sub_layout);
209 }
210 main_layout->addWidget(scroll_area);
211
212 auto info_layout = new QHBoxLayout;
213 {
214 auto xy_layout = new QVBoxLayout;
215 {
216 {
217 auto sub_layout = new QHBoxLayout;
218 sub_layout->addWidget(new QLabel(tr("X:")));
219 sub_layout->addWidget(surface_picker_x_control);
220 xy_layout->addLayout(sub_layout);
221 }
222 {
223 auto sub_layout = new QHBoxLayout;
224 sub_layout->addWidget(new QLabel(tr("Y:")));
225 sub_layout->addWidget(surface_picker_y_control);
226 xy_layout->addLayout(sub_layout);
227 }
228 }
229 info_layout->addLayout(xy_layout);
230 surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
231 info_layout->addWidget(surface_info_label);
232 }
233 main_layout->addLayout(info_layout);
234
235 main_layout->addWidget(save_surface);
236 main_widget->setLayout(main_layout);
237 setWidget(main_widget);
238
239 // Load current data - TODO: Make sure this works when emulation is not running
240 if (debug_context && debug_context->at_breakpoint) {
241 emit Update();
242 widget()->setEnabled(debug_context->at_breakpoint);
243 } else {
244 widget()->setEnabled(false);
245 }
246}
247
248void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) {
249 emit Update();
250 widget()->setEnabled(true);
251}
252
253void GraphicsSurfaceWidget::OnResumed() {
254 widget()->setEnabled(false);
255}
256
257void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
258 surface_source = static_cast<Source>(new_value);
259 emit Update();
260}
261
262void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
263 if (surface_address != new_value) {
264 surface_address = static_cast<GPUVAddr>(new_value);
265
266 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
267 emit Update();
268 }
269}
270
271void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) {
272 if (surface_width != static_cast<unsigned>(new_value)) {
273 surface_width = static_cast<unsigned>(new_value);
274
275 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
276 emit Update();
277 }
278}
279
280void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) {
281 if (surface_height != static_cast<unsigned>(new_value)) {
282 surface_height = static_cast<unsigned>(new_value);
283
284 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
285 emit Update();
286 }
287}
288
289void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) {
290 if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) {
291 surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value);
292
293 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
294 emit Update();
295 }
296}
297
298void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) {
299 if (surface_picker_x != new_value) {
300 surface_picker_x = new_value;
301 Pick(surface_picker_x, surface_picker_y);
302 }
303}
304
305void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) {
306 if (surface_picker_y != new_value) {
307 surface_picker_y = new_value;
308 Pick(surface_picker_x, surface_picker_y);
309 }
310}
311
312void GraphicsSurfaceWidget::Pick(int x, int y) {
313 surface_picker_x_control->setValue(x);
314 surface_picker_y_control->setValue(y);
315
316 if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 ||
317 y >= static_cast<int>(surface_height)) {
318 surface_info_label->setText(tr("Pixel out of bounds"));
319 surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
320 return;
321 }
322
323 surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>"));
324 surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
325}
326
327void GraphicsSurfaceWidget::OnUpdate() {
328 auto& gpu = Core::System::GetInstance().GPU();
329
330 QPixmap pixmap;
331
332 switch (surface_source) {
333 case Source::RenderTarget0:
334 case Source::RenderTarget1:
335 case Source::RenderTarget2:
336 case Source::RenderTarget3:
337 case Source::RenderTarget4:
338 case Source::RenderTarget5:
339 case Source::RenderTarget6:
340 case Source::RenderTarget7: {
341 // TODO: Store a reference to the registers in the debug context instead of accessing them
342 // directly...
343
344 const auto& registers = gpu.Maxwell3D().regs;
345 const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) -
346 static_cast<std::size_t>(Source::RenderTarget0)];
347
348 surface_address = rt.Address();
349 surface_width = rt.width;
350 surface_height = rt.height;
351 if (rt.format != Tegra::RenderTargetFormat::NONE) {
352 surface_format = ConvertToTextureFormat(rt.format);
353 }
354
355 break;
356 }
357
358 case Source::Custom: {
359 // Keep user-specified values
360 break;
361 }
362
363 default:
364 qDebug() << "Unknown surface source " << static_cast<int>(surface_source);
365 break;
366 }
367
368 surface_address_control->SetValue(surface_address);
369 surface_width_control->setValue(surface_width);
370 surface_height_control->setValue(surface_height);
371 surface_format_control->setCurrentIndex(static_cast<int>(surface_format));
372
373 if (surface_address == 0) {
374 surface_picture_label->hide();
375 surface_info_label->setText(tr("(invalid surface address)"));
376 surface_info_label->setAlignment(Qt::AlignCenter);
377 surface_picker_x_control->setEnabled(false);
378 surface_picker_y_control->setEnabled(false);
379 save_surface->setEnabled(false);
380 return;
381 }
382
383 // TODO: Implement a good way to visualize alpha components!
384
385 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
386
387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
390 gpu.MemoryManager().GetPointer(surface_address), 1, 1,
391 Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U);
392
393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
394 surface_width, surface_height);
395
396 surface_picture_label->show();
397
398 for (unsigned int y = 0; y < surface_height; ++y) {
399 for (unsigned int x = 0; x < surface_width; ++x) {
400 Common::Vec4<u8> color;
401 color[0] = texture_data[x + y * surface_width + 0];
402 color[1] = texture_data[x + y * surface_width + 1];
403 color[2] = texture_data[x + y * surface_width + 2];
404 color[3] = texture_data[x + y * surface_width + 3];
405 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
406 }
407 }
408
409 pixmap = QPixmap::fromImage(decoded_image);
410 surface_picture_label->setPixmap(pixmap);
411 surface_picture_label->resize(pixmap.size());
412
413 // Update the info with pixel data
414 surface_picker_x_control->setEnabled(true);
415 surface_picker_y_control->setEnabled(true);
416 Pick(surface_picker_x, surface_picker_y);
417
418 // Enable saving the converted pixmap to file
419 save_surface->setEnabled(true);
420}
421
422void GraphicsSurfaceWidget::SaveSurface() {
423 QString png_filter = tr("Portable Network Graphic (*.png)");
424 QString bin_filter = tr("Binary data (*.bin)");
425
426 QString selectedFilter;
427 QString filename = QFileDialog::getSaveFileName(
428 this, tr("Save Surface"),
429 QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
430 QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
431
432 if (filename.isEmpty()) {
433 // If the user canceled the dialog, don't save anything.
434 return;
435 }
436
437 if (selectedFilter == png_filter) {
438 const QPixmap* pixmap = surface_picture_label->pixmap();
439 ASSERT_MSG(pixmap != nullptr, "No pixmap set");
440
441 QFile file(filename);
442 file.open(QIODevice::WriteOnly);
443 if (pixmap)
444 pixmap->save(&file, "PNG");
445 } else if (selectedFilter == bin_filter) {
446 auto& gpu = Core::System::GetInstance().GPU();
447 std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
448
449 const u8* buffer = Memory::GetPointer(*address);
450 ASSERT_MSG(buffer != nullptr, "Memory not accessible");
451
452 QFile file(filename);
453 file.open(QIODevice::WriteOnly);
454 int size = surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format);
455 QByteArray data(reinterpret_cast<const char*>(buffer), size);
456 file.write(data);
457 } else {
458 UNREACHABLE_MSG("Unhandled filter selected");
459 }
460}
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h
deleted file mode 100644
index 89445b18f..000000000
--- a/src/yuzu/debugger/graphics/graphics_surface.h
+++ /dev/null
@@ -1,96 +0,0 @@
1// Copyright 2014 Citra 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 <QLabel>
8#include <QPushButton>
9#include "video_core/memory_manager.h"
10#include "video_core/textures/texture.h"
11#include "yuzu/debugger/graphics/graphics_breakpoint_observer.h"
12
13class QComboBox;
14class QSpinBox;
15class CSpinBox;
16
17class GraphicsSurfaceWidget;
18
19class SurfacePicture : public QLabel {
20 Q_OBJECT
21
22public:
23 explicit SurfacePicture(QWidget* parent = nullptr,
24 GraphicsSurfaceWidget* surface_widget = nullptr);
25 ~SurfacePicture() override;
26
27protected slots:
28 void mouseMoveEvent(QMouseEvent* event) override;
29 void mousePressEvent(QMouseEvent* event) override;
30
31private:
32 GraphicsSurfaceWidget* surface_widget;
33};
34
35class GraphicsSurfaceWidget : public BreakPointObserverDock {
36 Q_OBJECT
37
38 using Event = Tegra::DebugContext::Event;
39
40 enum class Source {
41 RenderTarget0 = 0,
42 RenderTarget1 = 1,
43 RenderTarget2 = 2,
44 RenderTarget3 = 3,
45 RenderTarget4 = 4,
46 RenderTarget5 = 5,
47 RenderTarget6 = 6,
48 RenderTarget7 = 7,
49 ZBuffer = 8,
50 Custom = 9,
51 };
52
53public:
54 explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
55 QWidget* parent = nullptr);
56 void Pick(int x, int y);
57
58public slots:
59 void OnSurfaceSourceChanged(int new_value);
60 void OnSurfaceAddressChanged(qint64 new_value);
61 void OnSurfaceWidthChanged(int new_value);
62 void OnSurfaceHeightChanged(int new_value);
63 void OnSurfaceFormatChanged(int new_value);
64 void OnSurfacePickerXChanged(int new_value);
65 void OnSurfacePickerYChanged(int new_value);
66 void OnUpdate();
67
68signals:
69 void Update();
70
71private:
72 void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
73 void OnResumed() override;
74
75 void SaveSurface();
76
77 QComboBox* surface_source_list;
78 CSpinBox* surface_address_control;
79 QSpinBox* surface_width_control;
80 QSpinBox* surface_height_control;
81 QComboBox* surface_format_control;
82
83 SurfacePicture* surface_picture_label;
84 QSpinBox* surface_picker_x_control;
85 QSpinBox* surface_picker_y_control;
86 QLabel* surface_info_label;
87 QPushButton* save_surface;
88
89 Source surface_source;
90 GPUVAddr surface_address;
91 unsigned surface_width;
92 unsigned surface_height;
93 Tegra::Texture::TextureFormat surface_format;
94 int surface_picker_x = 0;
95 int surface_picker_y = 0;
96};
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index c0e3c5fa9..b0ca766ec 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -18,6 +18,7 @@
18#include "common/common_types.h" 18#include "common/common_types.h"
19#include "common/logging/log.h" 19#include "common/logging/log.h"
20#include "core/file_sys/patch_manager.h" 20#include "core/file_sys/patch_manager.h"
21#include "core/file_sys/registered_cache.h"
21#include "yuzu/compatibility_list.h" 22#include "yuzu/compatibility_list.h"
22#include "yuzu/game_list.h" 23#include "yuzu/game_list.h"
23#include "yuzu/game_list_p.h" 24#include "yuzu/game_list_p.h"
@@ -193,8 +194,9 @@ void GameList::onFilterCloseClicked() {
193 main_window->filterBarSetChecked(false); 194 main_window->filterBarSetChecked(false);
194} 195}
195 196
196GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) 197GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvider* provider,
197 : QWidget{parent}, vfs(std::move(vfs)) { 198 GMainWindow* parent)
199 : QWidget{parent}, vfs(std::move(vfs)), provider(provider) {
198 watcher = new QFileSystemWatcher(this); 200 watcher = new QFileSystemWatcher(this);
199 connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); 201 connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
200 202
@@ -329,6 +331,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
329 QMenu context_menu; 331 QMenu context_menu;
330 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); 332 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
331 QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); 333 QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location"));
334 QAction* open_transferable_shader_cache =
335 context_menu.addAction(tr("Open Transferable Shader Cache"));
332 context_menu.addSeparator(); 336 context_menu.addSeparator();
333 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); 337 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
334 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 338 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
@@ -344,6 +348,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
344 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); 348 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); });
345 connect(open_lfs_location, &QAction::triggered, 349 connect(open_lfs_location, &QAction::triggered,
346 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); }); 350 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); });
351 connect(open_transferable_shader_cache, &QAction::triggered,
352 [&]() { emit OpenTransferableShaderCacheRequested(program_id); });
347 connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); }); 353 connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); });
348 connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); 354 connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); });
349 connect(navigate_to_gamedb_entry, &QAction::triggered, 355 connect(navigate_to_gamedb_entry, &QAction::triggered,
@@ -428,7 +434,8 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
428 434
429 emit ShouldCancelWorker(); 435 emit ShouldCancelWorker();
430 436
431 GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan, compatibility_list); 437 GameListWorker* worker =
438 new GameListWorker(vfs, provider, dir_path, deep_scan, compatibility_list);
432 439
433 connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); 440 connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
434 connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, 441 connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,
@@ -460,9 +467,10 @@ void GameList::LoadInterfaceLayout() {
460const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; 467const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"};
461 468
462void GameList::RefreshGameDirectory() { 469void GameList::RefreshGameDirectory() {
463 if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { 470 if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) {
464 LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); 471 LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
465 search_field->clear(); 472 search_field->clear();
466 PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 473 PopulateAsync(UISettings::values.game_directory_path,
474 UISettings::values.game_directory_deepscan);
467 } 475 }
468} 476}
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index b317eb2fc..56007eef8 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -26,8 +26,9 @@ class GameListSearchField;
26class GMainWindow; 26class GMainWindow;
27 27
28namespace FileSys { 28namespace FileSys {
29class ManualContentProvider;
29class VfsFilesystem; 30class VfsFilesystem;
30} 31} // namespace FileSys
31 32
32enum class GameListOpenTarget { 33enum class GameListOpenTarget {
33 SaveData, 34 SaveData,
@@ -47,7 +48,8 @@ public:
47 COLUMN_COUNT, // Number of columns 48 COLUMN_COUNT, // Number of columns
48 }; 49 };
49 50
50 explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, GMainWindow* parent = nullptr); 51 explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs,
52 FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr);
51 ~GameList() override; 53 ~GameList() override;
52 54
53 void clearFilter(); 55 void clearFilter();
@@ -66,6 +68,7 @@ signals:
66 void GameChosen(QString game_path); 68 void GameChosen(QString game_path);
67 void ShouldCancelWorker(); 69 void ShouldCancelWorker();
68 void OpenFolderRequested(u64 program_id, GameListOpenTarget target); 70 void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
71 void OpenTransferableShaderCacheRequested(u64 program_id);
69 void DumpRomFSRequested(u64 program_id, const std::string& game_path); 72 void DumpRomFSRequested(u64 program_id, const std::string& game_path);
70 void CopyTIDRequested(u64 program_id); 73 void CopyTIDRequested(u64 program_id);
71 void NavigateToGamedbEntryRequested(u64 program_id, 74 void NavigateToGamedbEntryRequested(u64 program_id,
@@ -85,6 +88,7 @@ private:
85 void RefreshGameDirectory(); 88 void RefreshGameDirectory();
86 89
87 std::shared_ptr<FileSys::VfsFilesystem> vfs; 90 std::shared_ptr<FileSys::VfsFilesystem> vfs;
91 FileSys::ManualContentProvider* provider;
88 GameListSearchField* search_field; 92 GameListSearchField* search_field;
89 GMainWindow* main_window = nullptr; 93 GMainWindow* main_window = nullptr;
90 QVBoxLayout* layout = nullptr; 94 QVBoxLayout* layout = nullptr;
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 3db0e90da..2cf5c58a0 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -95,7 +95,7 @@ public:
95 if (row2.isEmpty()) 95 if (row2.isEmpty())
96 return row1; 96 return row1;
97 97
98 return row1 + "\n " + row2; 98 return QString(row1 + "\n " + row2);
99 } 99 }
100 100
101 return GameListItem::data(role); 101 return GameListItem::data(role);
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index b37710f59..8687e7c5a 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -12,12 +12,15 @@
12 12
13#include "common/common_paths.h" 13#include "common/common_paths.h"
14#include "common/file_util.h" 14#include "common/file_util.h"
15#include "core/core.h"
16#include "core/file_sys/card_image.h"
15#include "core/file_sys/content_archive.h" 17#include "core/file_sys/content_archive.h"
16#include "core/file_sys/control_metadata.h" 18#include "core/file_sys/control_metadata.h"
17#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
18#include "core/file_sys/nca_metadata.h" 20#include "core/file_sys/nca_metadata.h"
19#include "core/file_sys/patch_manager.h" 21#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/registered_cache.h" 22#include "core/file_sys/registered_cache.h"
23#include "core/file_sys/submission_package.h"
21#include "core/hle/service/filesystem/filesystem.h" 24#include "core/hle/service/filesystem/filesystem.h"
22#include "core/loader/loader.h" 25#include "core/loader/loader.h"
23#include "yuzu/compatibility_list.h" 26#include "yuzu/compatibility_list.h"
@@ -119,20 +122,25 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
119} 122}
120} // Anonymous namespace 123} // Anonymous namespace
121 124
122GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan, 125GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs,
123 const CompatibilityList& compatibility_list) 126 FileSys::ManualContentProvider* provider, QString dir_path,
124 : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan), 127 bool deep_scan, const CompatibilityList& compatibility_list)
128 : vfs(std::move(vfs)), provider(provider), dir_path(std::move(dir_path)), deep_scan(deep_scan),
125 compatibility_list(compatibility_list) {} 129 compatibility_list(compatibility_list) {}
126 130
127GameListWorker::~GameListWorker() = default; 131GameListWorker::~GameListWorker() = default;
128 132
129void GameListWorker::AddInstalledTitlesToGameList() { 133void GameListWorker::AddTitlesToGameList() {
130 const auto cache = Service::FileSystem::GetUnionContents(); 134 const auto& cache = dynamic_cast<FileSys::ContentProviderUnion&>(
131 const auto installed_games = cache.ListEntriesFilter(FileSys::TitleType::Application, 135 Core::System::GetInstance().GetContentProvider());
132 FileSys::ContentRecordType::Program); 136 const auto installed_games = cache.ListEntriesFilterOrigin(
137 std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
133 138
134 for (const auto& game : installed_games) { 139 for (const auto& [slot, game] : installed_games) {
135 const auto file = cache.GetEntryUnparsed(game); 140 if (slot == FileSys::ContentProviderUnionSlot::FrontendManual)
141 continue;
142
143 const auto file = cache.GetEntryUnparsed(game.title_id, game.type);
136 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); 144 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
137 if (!loader) 145 if (!loader)
138 continue; 146 continue;
@@ -150,45 +158,13 @@ void GameListWorker::AddInstalledTitlesToGameList() {
150 emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, 158 emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,
151 compatibility_list, patch)); 159 compatibility_list, patch));
152 } 160 }
153
154 const auto control_data = cache.ListEntriesFilter(FileSys::TitleType::Application,
155 FileSys::ContentRecordType::Control);
156
157 for (const auto& entry : control_data) {
158 auto nca = cache.GetEntry(entry);
159 if (nca != nullptr) {
160 nca_control_map.insert_or_assign(entry.title_id, std::move(nca));
161 }
162 }
163} 161}
164 162
165void GameListWorker::FillControlMap(const std::string& dir_path) { 163void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path,
166 const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory, 164 unsigned int recursion) {
167 const std::string& virtual_name) -> bool { 165 const auto callback = [this, target, recursion](u64* num_entries_out,
168 if (stop_processing) { 166 const std::string& directory,
169 // Breaks the callback loop 167 const std::string& virtual_name) -> bool {
170 return false;
171 }
172
173 const std::string physical_name = directory + DIR_SEP + virtual_name;
174 const QFileInfo file_info(QString::fromStdString(physical_name));
175 if (!file_info.isDir() && file_info.suffix() == QStringLiteral("nca")) {
176 auto nca =
177 std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
178 if (nca->GetType() == FileSys::NCAContentType::Control) {
179 const u64 title_id = nca->GetTitleId();
180 nca_control_map.insert_or_assign(title_id, std::move(nca));
181 }
182 }
183 return true;
184 };
185
186 FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback);
187}
188
189void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
190 const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory,
191 const std::string& virtual_name) -> bool {
192 if (stop_processing) { 168 if (stop_processing) {
193 // Breaks the callback loop. 169 // Breaks the callback loop.
194 return false; 170 return false;
@@ -198,7 +174,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
198 const bool is_dir = FileUtil::IsDirectory(physical_name); 174 const bool is_dir = FileUtil::IsDirectory(physical_name);
199 if (!is_dir && 175 if (!is_dir &&
200 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { 176 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
201 auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); 177 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read);
178 auto loader = Loader::GetLoader(file);
202 if (!loader) { 179 if (!loader) {
203 return true; 180 return true;
204 } 181 }
@@ -209,31 +186,42 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
209 return true; 186 return true;
210 } 187 }
211 188
212 std::vector<u8> icon;
213 const auto res1 = loader->ReadIcon(icon);
214
215 u64 program_id = 0; 189 u64 program_id = 0;
216 const auto res2 = loader->ReadProgramId(program_id); 190 const auto res2 = loader->ReadProgramId(program_id);
217 191
218 std::string name = " "; 192 if (target == ScanTarget::FillManualContentProvider) {
219 const auto res3 = loader->ReadTitle(name); 193 if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
194 provider->AddEntry(FileSys::TitleType::Application,
195 FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
196 program_id, file);
197 } else if (res2 == Loader::ResultStatus::Success &&
198 (file_type == Loader::FileType::XCI ||
199 file_type == Loader::FileType::NSP)) {
200 const auto nsp = file_type == Loader::FileType::NSP
201 ? std::make_shared<FileSys::NSP>(file)
202 : FileSys::XCI{file}.GetSecurePartitionNSP();
203 for (const auto& title : nsp->GetNCAs()) {
204 for (const auto& entry : title.second) {
205 provider->AddEntry(entry.first.first, entry.first.second, title.first,
206 entry.second->GetBaseFile());
207 }
208 }
209 }
210 } else {
211 std::vector<u8> icon;
212 const auto res1 = loader->ReadIcon(icon);
220 213
221 const FileSys::PatchManager patch{program_id}; 214 std::string name = " ";
215 const auto res3 = loader->ReadTitle(name);
222 216
223 if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && 217 const FileSys::PatchManager patch{program_id};
224 res2 == Loader::ResultStatus::Success) {
225 // Use from metadata pool.
226 if (nca_control_map.find(program_id) != nca_control_map.end()) {
227 const auto& nca = nca_control_map[program_id];
228 GetMetadataFromControlNCA(patch, *nca, icon, name);
229 }
230 }
231 218
232 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, 219 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id,
233 compatibility_list, patch)); 220 compatibility_list, patch));
221 }
234 } else if (is_dir && recursion > 0) { 222 } else if (is_dir && recursion > 0) {
235 watch_list.append(QString::fromStdString(physical_name)); 223 watch_list.append(QString::fromStdString(physical_name));
236 AddFstEntriesToGameList(physical_name, recursion - 1); 224 ScanFileSystem(target, physical_name, recursion - 1);
237 } 225 }
238 226
239 return true; 227 return true;
@@ -245,10 +233,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
245void GameListWorker::run() { 233void GameListWorker::run() {
246 stop_processing = false; 234 stop_processing = false;
247 watch_list.append(dir_path); 235 watch_list.append(dir_path);
248 FillControlMap(dir_path.toStdString()); 236 provider->ClearAllEntries();
249 AddInstalledTitlesToGameList(); 237 ScanFileSystem(ScanTarget::FillManualContentProvider, dir_path.toStdString(),
250 AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); 238 deep_scan ? 256 : 0);
251 nca_control_map.clear(); 239 AddTitlesToGameList();
240 ScanFileSystem(ScanTarget::PopulateGameList, dir_path.toStdString(), deep_scan ? 256 : 0);
252 emit Finished(watch_list); 241 emit Finished(watch_list);
253} 242}
254 243
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
index 0e42d0bde..7c3074af9 100644
--- a/src/yuzu/game_list_worker.h
+++ b/src/yuzu/game_list_worker.h
@@ -33,7 +33,8 @@ class GameListWorker : public QObject, public QRunnable {
33 Q_OBJECT 33 Q_OBJECT
34 34
35public: 35public:
36 GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan, 36 GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs,
37 FileSys::ManualContentProvider* provider, QString dir_path, bool deep_scan,
37 const CompatibilityList& compatibility_list); 38 const CompatibilityList& compatibility_list);
38 ~GameListWorker() override; 39 ~GameListWorker() override;
39 40
@@ -58,12 +59,17 @@ signals:
58 void Finished(QStringList watch_list); 59 void Finished(QStringList watch_list);
59 60
60private: 61private:
61 void AddInstalledTitlesToGameList(); 62 void AddTitlesToGameList();
62 void FillControlMap(const std::string& dir_path); 63
63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); 64 enum class ScanTarget {
65 FillManualContentProvider,
66 PopulateGameList,
67 };
68
69 void ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion = 0);
64 70
65 std::shared_ptr<FileSys::VfsFilesystem> vfs; 71 std::shared_ptr<FileSys::VfsFilesystem> vfs;
66 std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map; 72 FileSys::ManualContentProvider* provider;
67 QStringList watch_list; 73 QStringList watch_list;
68 QString dir_path; 74 QString dir_path;
69 bool deep_scan; 75 bool deep_scan;
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index dce399774..4582e7f21 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -2,7 +2,6 @@
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 <map>
6#include <QKeySequence> 5#include <QKeySequence>
7#include <QShortcut> 6#include <QShortcut>
8#include <QTreeWidgetItem> 7#include <QTreeWidgetItem>
@@ -13,47 +12,32 @@
13HotkeyRegistry::HotkeyRegistry() = default; 12HotkeyRegistry::HotkeyRegistry() = default;
14HotkeyRegistry::~HotkeyRegistry() = default; 13HotkeyRegistry::~HotkeyRegistry() = default;
15 14
16void HotkeyRegistry::LoadHotkeys() {
17 // Make sure NOT to use a reference here because it would become invalid once we call
18 // beginGroup()
19 for (auto shortcut : UISettings::values.shortcuts) {
20 const QStringList cat = shortcut.first.split('/');
21 Q_ASSERT(cat.size() >= 2);
22
23 // RegisterHotkey assigns default keybindings, so use old values as default parameters
24 Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
25 if (!shortcut.second.first.isEmpty()) {
26 hk.keyseq = QKeySequence::fromString(shortcut.second.first);
27 hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second);
28 }
29 if (hk.shortcut)
30 hk.shortcut->setKey(hk.keyseq);
31 }
32}
33
34void HotkeyRegistry::SaveHotkeys() { 15void HotkeyRegistry::SaveHotkeys() {
35 UISettings::values.shortcuts.clear(); 16 UISettings::values.shortcuts.clear();
36 for (const auto& group : hotkey_groups) { 17 for (const auto& group : hotkey_groups) {
37 for (const auto& hotkey : group.second) { 18 for (const auto& hotkey : group.second) {
38 UISettings::values.shortcuts.emplace_back( 19 UISettings::values.shortcuts.push_back(
39 UISettings::Shortcut(group.first + '/' + hotkey.first, 20 {hotkey.first, group.first,
40 UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), 21 UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
41 hotkey.second.context))); 22 hotkey.second.context)});
42 } 23 }
43 } 24 }
44} 25}
45 26
46void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action, 27void HotkeyRegistry::LoadHotkeys() {
47 const QKeySequence& default_keyseq, 28 // Make sure NOT to use a reference here because it would become invalid once we call
48 Qt::ShortcutContext default_context) { 29 // beginGroup()
49 auto& hotkey_group = hotkey_groups[group]; 30 for (auto shortcut : UISettings::values.shortcuts) {
50 if (hotkey_group.find(action) != hotkey_group.end()) { 31 Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
51 return; 32 if (!shortcut.shortcut.first.isEmpty()) {
33 hk.keyseq = QKeySequence::fromString(shortcut.shortcut.first, QKeySequence::NativeText);
34 hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.second);
35 }
36 if (hk.shortcut) {
37 hk.shortcut->disconnect();
38 hk.shortcut->setKey(hk.keyseq);
39 }
52 } 40 }
53
54 auto& hotkey_action = hotkey_groups[group][action];
55 hotkey_action.keyseq = default_keyseq;
56 hotkey_action.context = default_context;
57} 41}
58 42
59QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { 43QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) {
@@ -65,24 +49,11 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action
65 return hk.shortcut; 49 return hk.shortcut;
66} 50}
67 51
68GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) { 52QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) {
69 ui.setupUi(this); 53 return hotkey_groups[group][action].keyseq;
70} 54}
71 55
72void GHotkeysDialog::Populate(const HotkeyRegistry& registry) { 56Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group,
73 for (const auto& group : registry.hotkey_groups) { 57 const QString& action) {
74 QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); 58 return hotkey_groups[group][action].context;
75 for (const auto& hotkey : group.second) {
76 QStringList columns;
77 columns << hotkey.first << hotkey.second.keyseq.toString();
78 QTreeWidgetItem* item = new QTreeWidgetItem(columns);
79 toplevel_item->addChild(item);
80 }
81 ui.treeWidget->addTopLevelItem(toplevel_item);
82 }
83 // TODO: Make context configurable as well (hiding the column for now)
84 ui.treeWidget->setColumnCount(2);
85
86 ui.treeWidget->resizeColumnToContents(0);
87 ui.treeWidget->resizeColumnToContents(1);
88} 59}
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index f38e6c002..4f526dc7e 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <map> 7#include <map>
8#include "ui_hotkeys.h"
9 8
10class QDialog; 9class QDialog;
11class QKeySequence; 10class QKeySequence;
@@ -14,7 +13,7 @@ class QShortcut;
14 13
15class HotkeyRegistry final { 14class HotkeyRegistry final {
16public: 15public:
17 friend class GHotkeysDialog; 16 friend class ConfigureHotkeys;
18 17
19 explicit HotkeyRegistry(); 18 explicit HotkeyRegistry();
20 ~HotkeyRegistry(); 19 ~HotkeyRegistry();
@@ -49,22 +48,27 @@ public:
49 QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); 48 QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
50 49
51 /** 50 /**
52 * Register a hotkey. 51 * Returns a QKeySequence object whose signal can be connected to QAction::setShortcut.
53 * 52 *
54 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") 53 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
55 * @param action Name of the action (e.g. "Start Emulation", "Load Image") 54 * @param action Name of the action (e.g. "Start Emulation", "Load Image").
56 * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the 55 */
57 * settings file before 56 QKeySequence GetKeySequence(const QString& group, const QString& action);
58 * @param default_context Default context to assign if the hotkey wasn't present in the settings 57
59 * file before 58 /**
60 * @warning Both the group and action strings will be displayed in the hotkey settings dialog 59 * Returns a Qt::ShortcutContext object who can be connected to other
60 * QAction::setShortcutContext.
61 *
62 * @param group General group this shortcut context belongs to (e.g. "Main Window",
63 * "Debugger").
64 * @param action Name of the action (e.g. "Start Emulation", "Load Image").
61 */ 65 */
62 void RegisterHotkey(const QString& group, const QString& action, 66 Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action);
63 const QKeySequence& default_keyseq = {},
64 Qt::ShortcutContext default_context = Qt::WindowShortcut);
65 67
66private: 68private:
67 struct Hotkey { 69 struct Hotkey {
70 Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
71
68 QKeySequence keyseq; 72 QKeySequence keyseq;
69 QShortcut* shortcut = nullptr; 73 QShortcut* shortcut = nullptr;
70 Qt::ShortcutContext context = Qt::WindowShortcut; 74 Qt::ShortcutContext context = Qt::WindowShortcut;
@@ -75,15 +79,3 @@ private:
75 79
76 HotkeyGroupMap hotkey_groups; 80 HotkeyGroupMap hotkey_groups;
77}; 81};
78
79class GHotkeysDialog : public QWidget {
80 Q_OBJECT
81
82public:
83 explicit GHotkeysDialog(QWidget* parent = nullptr);
84
85 void Populate(const HotkeyRegistry& registry);
86
87private:
88 Ui::hotkeys ui;
89};
diff --git a/src/yuzu/hotkeys.ui b/src/yuzu/hotkeys.ui
deleted file mode 100644
index 050fe064e..000000000
--- a/src/yuzu/hotkeys.ui
+++ /dev/null
@@ -1,46 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>hotkeys</class>
4 <widget class="QWidget" name="hotkeys">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>363</width>
10 <height>388</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Hotkey Settings</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QTreeWidget" name="treeWidget">
19 <property name="selectionBehavior">
20 <enum>QAbstractItemView::SelectItems</enum>
21 </property>
22 <property name="headerHidden">
23 <bool>false</bool>
24 </property>
25 <column>
26 <property name="text">
27 <string>Action</string>
28 </property>
29 </column>
30 <column>
31 <property name="text">
32 <string>Hotkey</string>
33 </property>
34 </column>
35 <column>
36 <property name="text">
37 <string>Context</string>
38 </property>
39 </column>
40 </widget>
41 </item>
42 </layout>
43 </widget>
44 <resources/>
45 <connections/>
46</ui>
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index 86f6d0165..4e2d988cd 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -192,7 +192,12 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
192 } 192 }
193 193
194 // update labels and progress bar 194 // update labels and progress bar
195 ui->stage->setText(stage_translations[stage].arg(value).arg(total)); 195 if (stage == VideoCore::LoadCallbackStage::Decompile ||
196 stage == VideoCore::LoadCallbackStage::Build) {
197 ui->stage->setText(stage_translations[stage].arg(value).arg(total));
198 } else {
199 ui->stage->setText(stage_translations[stage]);
200 }
196 ui->value->setText(estimate); 201 ui->value->setText(estimate);
197 ui->progress_bar->setValue(static_cast<int>(value)); 202 ui->progress_bar->setValue(static_cast<int>(value));
198 previous_time = now; 203 previous_time = now;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 41ba3c4c6..bdee44b04 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -37,14 +37,20 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
37#include <glad/glad.h> 37#include <glad/glad.h>
38 38
39#define QT_NO_OPENGL 39#define QT_NO_OPENGL
40#include <QClipboard>
41#include <QDesktopServices>
40#include <QDesktopWidget> 42#include <QDesktopWidget>
41#include <QDialogButtonBox> 43#include <QDialogButtonBox>
42#include <QFile> 44#include <QFile>
43#include <QFileDialog> 45#include <QFileDialog>
46#include <QInputDialog>
44#include <QMessageBox> 47#include <QMessageBox>
48#include <QProgressBar>
49#include <QProgressDialog>
50#include <QShortcut>
51#include <QStatusBar>
45#include <QtConcurrent/QtConcurrent> 52#include <QtConcurrent/QtConcurrent>
46#include <QtGui> 53
47#include <QtWidgets>
48#include <fmt/format.h> 54#include <fmt/format.h>
49#include "common/common_paths.h" 55#include "common/common_paths.h"
50#include "common/detached_tasks.h" 56#include "common/detached_tasks.h"
@@ -55,11 +61,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
55#include "common/microprofile.h" 61#include "common/microprofile.h"
56#include "common/scm_rev.h" 62#include "common/scm_rev.h"
57#include "common/scope_exit.h" 63#include "common/scope_exit.h"
58#include "common/string_util.h"
59#include "common/telemetry.h" 64#include "common/telemetry.h"
60#include "core/core.h" 65#include "core/core.h"
61#include "core/crypto/key_manager.h" 66#include "core/crypto/key_manager.h"
62#include "core/file_sys/bis_factory.h"
63#include "core/file_sys/card_image.h" 67#include "core/file_sys/card_image.h"
64#include "core/file_sys/content_archive.h" 68#include "core/file_sys/content_archive.h"
65#include "core/file_sys/control_metadata.h" 69#include "core/file_sys/control_metadata.h"
@@ -71,7 +75,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
71#include "core/frontend/applets/software_keyboard.h" 75#include "core/frontend/applets/software_keyboard.h"
72#include "core/hle/kernel/process.h" 76#include "core/hle/kernel/process.h"
73#include "core/hle/service/filesystem/filesystem.h" 77#include "core/hle/service/filesystem/filesystem.h"
74#include "core/hle/service/filesystem/fsp_ldr.h"
75#include "core/hle/service/nfp/nfp.h" 78#include "core/hle/service/nfp/nfp.h"
76#include "core/hle/service/sm/sm.h" 79#include "core/hle/service/sm/sm.h"
77#include "core/loader/loader.h" 80#include "core/loader/loader.h"
@@ -87,7 +90,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
87#include "yuzu/configuration/configure_dialog.h" 90#include "yuzu/configuration/configure_dialog.h"
88#include "yuzu/debugger/console.h" 91#include "yuzu/debugger/console.h"
89#include "yuzu/debugger/graphics/graphics_breakpoints.h" 92#include "yuzu/debugger/graphics/graphics_breakpoints.h"
90#include "yuzu/debugger/graphics/graphics_surface.h"
91#include "yuzu/debugger/profiler.h" 93#include "yuzu/debugger/profiler.h"
92#include "yuzu/debugger/wait_tree.h" 94#include "yuzu/debugger/wait_tree.h"
93#include "yuzu/discord.h" 95#include "yuzu/discord.h"
@@ -168,7 +170,8 @@ static void InitializeLogging() {
168 170
169GMainWindow::GMainWindow() 171GMainWindow::GMainWindow()
170 : config(new Config()), emu_thread(nullptr), 172 : config(new Config()), emu_thread(nullptr),
171 vfs(std::make_shared<FileSys::RealVfsFilesystem>()) { 173 vfs(std::make_shared<FileSys::RealVfsFilesystem>()),
174 provider(std::make_unique<FileSys::ManualContentProvider>()) {
172 InitializeLogging(); 175 InitializeLogging();
173 176
174 debug_context = Tegra::DebugContext::Construct(); 177 debug_context = Tegra::DebugContext::Construct();
@@ -200,13 +203,18 @@ GMainWindow::GMainWindow()
200 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); 203 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
201 show(); 204 show();
202 205
206 Core::System::GetInstance().SetContentProvider(
207 std::make_unique<FileSys::ContentProviderUnion>());
208 Core::System::GetInstance().RegisterContentProvider(
209 FileSys::ContentProviderUnionSlot::FrontendManual, provider.get());
210 Service::FileSystem::CreateFactories(*vfs);
211
203 // Gen keys if necessary 212 // Gen keys if necessary
204 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); 213 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
205 214
206 // Necessary to load titles from nand in gamelist.
207 Service::FileSystem::CreateFactories(*vfs);
208 game_list->LoadCompatibilityList(); 215 game_list->LoadCompatibilityList();
209 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 216 game_list->PopulateAsync(UISettings::values.game_directory_path,
217 UISettings::values.game_directory_deepscan);
210 218
211 // Show one-time "callout" messages to the user 219 // Show one-time "callout" messages to the user
212 ShowTelemetryCallout(); 220 ShowTelemetryCallout();
@@ -416,7 +424,7 @@ void GMainWindow::InitializeWidgets() {
416 render_window = new GRenderWindow(this, emu_thread.get()); 424 render_window = new GRenderWindow(this, emu_thread.get());
417 render_window->hide(); 425 render_window->hide();
418 426
419 game_list = new GameList(vfs, this); 427 game_list = new GameList(vfs, provider.get(), this);
420 ui.horizontalLayout->addWidget(game_list); 428 ui.horizontalLayout->addWidget(game_list);
421 429
422 loading_screen = new LoadingScreen(this); 430 loading_screen = new LoadingScreen(this);
@@ -475,11 +483,6 @@ void GMainWindow::InitializeDebugWidgets() {
475 graphicsBreakpointsWidget->hide(); 483 graphicsBreakpointsWidget->hide();
476 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); 484 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
477 485
478 graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this);
479 addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget);
480 graphicsSurfaceWidget->hide();
481 debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction());
482
483 waitTreeWidget = new WaitTreeWidget(this); 486 waitTreeWidget = new WaitTreeWidget(this);
484 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); 487 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
485 waitTreeWidget->hide(); 488 waitTreeWidget->hide();
@@ -511,33 +514,34 @@ void GMainWindow::InitializeRecentFileMenuActions() {
511} 514}
512 515
513void GMainWindow::InitializeHotkeys() { 516void GMainWindow::InitializeHotkeys() {
514 hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
515 hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
516 hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
517 hotkey_registry.RegisterHotkey("Main Window", "Restart", QKeySequence(Qt::Key_F5));
518 hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
519 hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
520 Qt::ApplicationShortcut);
521 hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
522 Qt::ApplicationShortcut);
523 hotkey_registry.RegisterHotkey("Main Window", "Increase Speed Limit", QKeySequence("+"),
524 Qt::ApplicationShortcut);
525 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
526 Qt::ApplicationShortcut);
527 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
528 Qt::ApplicationShortcut);
529 hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot",
530 QKeySequence(QKeySequence::Print));
531 hotkey_registry.RegisterHotkey("Main Window", "Change Docked Mode", QKeySequence(Qt::Key_F10));
532
533 hotkey_registry.LoadHotkeys(); 517 hotkey_registry.LoadHotkeys();
534 518
519 ui.action_Load_File->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Load File"));
520 ui.action_Load_File->setShortcutContext(
521 hotkey_registry.GetShortcutContext("Main Window", "Load File"));
522
523 ui.action_Exit->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Exit yuzu"));
524 ui.action_Exit->setShortcutContext(
525 hotkey_registry.GetShortcutContext("Main Window", "Exit yuzu"));
526
527 ui.action_Stop->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Stop Emulation"));
528 ui.action_Stop->setShortcutContext(
529 hotkey_registry.GetShortcutContext("Main Window", "Stop Emulation"));
530
531 ui.action_Show_Filter_Bar->setShortcut(
532 hotkey_registry.GetKeySequence("Main Window", "Toggle Filter Bar"));
533 ui.action_Show_Filter_Bar->setShortcutContext(
534 hotkey_registry.GetShortcutContext("Main Window", "Toggle Filter Bar"));
535
536 ui.action_Show_Status_Bar->setShortcut(
537 hotkey_registry.GetKeySequence("Main Window", "Toggle Status Bar"));
538 ui.action_Show_Status_Bar->setShortcutContext(
539 hotkey_registry.GetShortcutContext("Main Window", "Toggle Status Bar"));
540
535 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, 541 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
536 this, &GMainWindow::OnMenuLoadFile); 542 this, &GMainWindow::OnMenuLoadFile);
537 connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this), 543 connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause Emulation", this),
538 &QShortcut::activated, this, &GMainWindow::OnStartGame); 544 &QShortcut::activated, this, [&] {
539 connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated,
540 this, [&] {
541 if (emulation_running) { 545 if (emulation_running) {
542 if (emu_thread->IsRunning()) { 546 if (emu_thread->IsRunning()) {
543 OnPauseGame(); 547 OnPauseGame();
@@ -546,8 +550,8 @@ void GMainWindow::InitializeHotkeys() {
546 } 550 }
547 } 551 }
548 }); 552 });
549 connect(hotkey_registry.GetHotkey("Main Window", "Restart", this), &QShortcut::activated, this, 553 connect(hotkey_registry.GetHotkey("Main Window", "Restart Emulation", this),
550 [this] { 554 &QShortcut::activated, this, [this] {
551 if (!Core::System::GetInstance().IsPoweredOn()) 555 if (!Core::System::GetInstance().IsPoweredOn())
552 return; 556 return;
553 BootGame(QString(game_path)); 557 BootGame(QString(game_path));
@@ -648,6 +652,8 @@ void GMainWindow::RestoreUIState() {
648void GMainWindow::ConnectWidgetEvents() { 652void GMainWindow::ConnectWidgetEvents() {
649 connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); 653 connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile);
650 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); 654 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
655 connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this,
656 &GMainWindow::OnTransferableShaderCacheOpenFile);
651 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); 657 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
652 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); 658 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
653 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, 659 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
@@ -692,7 +698,6 @@ void GMainWindow::ConnectMenuEvents() {
692 &GMainWindow::ToggleWindowMode); 698 &GMainWindow::ToggleWindowMode);
693 connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this, 699 connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this,
694 &GMainWindow::OnDisplayTitleBars); 700 &GMainWindow::OnDisplayTitleBars);
695 ui.action_Show_Filter_Bar->setShortcut(tr("CTRL+F"));
696 connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); 701 connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar);
697 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); 702 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
698 703
@@ -1082,6 +1087,39 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1082 QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); 1087 QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
1083} 1088}
1084 1089
1090void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
1091 ASSERT(program_id != 0);
1092
1093 const QString tranferable_shader_cache_folder_path =
1094 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) + "opengl" +
1095 DIR_SEP + "transferable";
1096
1097 const QString transferable_shader_cache_file_path =
1098 tranferable_shader_cache_folder_path + DIR_SEP +
1099 QString::fromStdString(fmt::format("{:016X}.bin", program_id));
1100
1101 if (!QFile::exists(transferable_shader_cache_file_path)) {
1102 QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"),
1103 tr("A shader cache for this title does not exist."));
1104 return;
1105 }
1106
1107 // Windows supports opening a folder with selecting a specified file in explorer. On every other
1108 // OS we just open the transferable shader cache folder without preselecting the transferable
1109 // shader cache file for the selected game.
1110#if defined(Q_OS_WIN)
1111 const QString explorer = QStringLiteral("explorer");
1112 QStringList param;
1113 if (!QFileInfo(transferable_shader_cache_file_path).isDir()) {
1114 param << QStringLiteral("/select,");
1115 }
1116 param << QDir::toNativeSeparators(transferable_shader_cache_file_path);
1117 QProcess::startDetached(explorer, param);
1118#else
1119 QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path));
1120#endif
1121}
1122
1085static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { 1123static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) {
1086 std::size_t out = 0; 1124 std::size_t out = 0;
1087 1125
@@ -1141,7 +1179,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1141 return; 1179 return;
1142 } 1180 }
1143 1181
1144 const auto installed = Service::FileSystem::GetUnionContents(); 1182 const auto& installed = Core::System::GetInstance().GetContentProvider();
1145 const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); 1183 const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);
1146 1184
1147 if (!romfs_title_id) { 1185 if (!romfs_title_id) {
@@ -1241,8 +1279,8 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1241 1279
1242 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); 1280 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
1243 if (reload) { 1281 if (reload) {
1244 game_list->PopulateAsync(UISettings::values.gamedir, 1282 game_list->PopulateAsync(UISettings::values.game_directory_path,
1245 UISettings::values.gamedir_deepscan); 1283 UISettings::values.game_directory_deepscan);
1246 } 1284 }
1247 1285
1248 config->Save(); 1286 config->Save();
@@ -1330,7 +1368,8 @@ void GMainWindow::OnMenuInstallToNAND() {
1330 const auto success = [this]() { 1368 const auto success = [this]() {
1331 QMessageBox::information(this, tr("Successfully Installed"), 1369 QMessageBox::information(this, tr("Successfully Installed"),
1332 tr("The file was successfully installed.")); 1370 tr("The file was successfully installed."));
1333 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1371 game_list->PopulateAsync(UISettings::values.game_directory_path,
1372 UISettings::values.game_directory_deepscan);
1334 }; 1373 };
1335 1374
1336 const auto failed = [this]() { 1375 const auto failed = [this]() {
@@ -1457,8 +1496,8 @@ void GMainWindow::OnMenuInstallToNAND() {
1457void GMainWindow::OnMenuSelectGameListRoot() { 1496void GMainWindow::OnMenuSelectGameListRoot() {
1458 QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); 1497 QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
1459 if (!dir_path.isEmpty()) { 1498 if (!dir_path.isEmpty()) {
1460 UISettings::values.gamedir = dir_path; 1499 UISettings::values.game_directory_path = dir_path;
1461 game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan); 1500 game_list->PopulateAsync(dir_path, UISettings::values.game_directory_deepscan);
1462 } 1501 }
1463} 1502}
1464 1503
@@ -1480,7 +1519,8 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
1480 : FileUtil::UserPath::NANDDir, 1519 : FileUtil::UserPath::NANDDir,
1481 dir_path.toStdString()); 1520 dir_path.toStdString());
1482 Service::FileSystem::CreateFactories(*vfs); 1521 Service::FileSystem::CreateFactories(*vfs);
1483 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1522 game_list->PopulateAsync(UISettings::values.game_directory_path,
1523 UISettings::values.game_directory_deepscan);
1484 } 1524 }
1485} 1525}
1486 1526
@@ -1624,6 +1664,7 @@ void GMainWindow::OnConfigure() {
1624 auto result = configureDialog.exec(); 1664 auto result = configureDialog.exec();
1625 if (result == QDialog::Accepted) { 1665 if (result == QDialog::Accepted) {
1626 configureDialog.applyConfiguration(); 1666 configureDialog.applyConfiguration();
1667 InitializeHotkeys();
1627 if (UISettings::values.theme != old_theme) 1668 if (UISettings::values.theme != old_theme)
1628 UpdateUITheme(); 1669 UpdateUITheme();
1629 if (UISettings::values.enable_discord_presence != old_discord_presence) 1670 if (UISettings::values.enable_discord_presence != old_discord_presence)
@@ -1631,8 +1672,8 @@ void GMainWindow::OnConfigure() {
1631 1672
1632 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); 1673 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
1633 if (reload) { 1674 if (reload) {
1634 game_list->PopulateAsync(UISettings::values.gamedir, 1675 game_list->PopulateAsync(UISettings::values.game_directory_path,
1635 UISettings::values.gamedir_deepscan); 1676 UISettings::values.game_directory_deepscan);
1636 } 1677 }
1637 1678
1638 config->Save(); 1679 config->Save();
@@ -1882,18 +1923,19 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1882 Service::FileSystem::CreateFactories(*vfs); 1923 Service::FileSystem::CreateFactories(*vfs);
1883 1924
1884 if (behavior == ReinitializeKeyBehavior::Warning) { 1925 if (behavior == ReinitializeKeyBehavior::Warning) {
1885 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1926 game_list->PopulateAsync(UISettings::values.game_directory_path,
1927 UISettings::values.game_directory_deepscan);
1886 } 1928 }
1887} 1929}
1888 1930
1889std::optional<u64> GMainWindow::SelectRomFSDumpTarget( 1931std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed,
1890 const FileSys::RegisteredCacheUnion& installed, u64 program_id) { 1932 u64 program_id) {
1891 const auto dlc_entries = 1933 const auto dlc_entries =
1892 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); 1934 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
1893 std::vector<FileSys::RegisteredCacheEntry> dlc_match; 1935 std::vector<FileSys::ContentProviderEntry> dlc_match;
1894 dlc_match.reserve(dlc_entries.size()); 1936 dlc_match.reserve(dlc_entries.size());
1895 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), 1937 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
1896 [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) { 1938 [&program_id, &installed](const FileSys::ContentProviderEntry& entry) {
1897 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id && 1939 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id &&
1898 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; 1940 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
1899 }); 1941 });
@@ -1989,6 +2031,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
1989 event->acceptProposedAction(); 2031 event->acceptProposedAction();
1990} 2032}
1991 2033
2034void GMainWindow::keyPressEvent(QKeyEvent* event) {
2035 if (render_window) {
2036 render_window->ForwardKeyPressEvent(event);
2037 }
2038}
2039
2040void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
2041 if (render_window) {
2042 render_window->ForwardKeyReleaseEvent(event);
2043 }
2044}
2045
1992bool GMainWindow::ConfirmChangeGame() { 2046bool GMainWindow::ConfirmChangeGame() {
1993 if (emu_thread == nullptr) 2047 if (emu_thread == nullptr)
1994 return true; 2048 return true;
@@ -2056,7 +2110,8 @@ int main(int argc, char* argv[]) {
2056 QCoreApplication::setOrganizationName("yuzu team"); 2110 QCoreApplication::setOrganizationName("yuzu team");
2057 QCoreApplication::setApplicationName("yuzu"); 2111 QCoreApplication::setApplicationName("yuzu");
2058 2112
2059 QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 2113 // Enables the core to make the qt created contexts current on std::threads
2114 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
2060 QApplication app(argc, argv); 2115 QApplication app(argc, argv);
2061 2116
2062 // Qt changes the locale and causes issues in float conversion using std::to_string() when 2117 // Qt changes the locale and causes issues in float conversion using std::to_string() when
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index e07c892cf..ce5045819 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -23,7 +23,6 @@ class EmuThread;
23class GameList; 23class GameList;
24class GImageInfo; 24class GImageInfo;
25class GraphicsBreakPointsWidget; 25class GraphicsBreakPointsWidget;
26class GraphicsSurfaceWidget;
27class GRenderWindow; 26class GRenderWindow;
28class LoadingScreen; 27class LoadingScreen;
29class MicroProfileDialog; 28class MicroProfileDialog;
@@ -37,7 +36,8 @@ struct SoftwareKeyboardParameters;
37} // namespace Core::Frontend 36} // namespace Core::Frontend
38 37
39namespace FileSys { 38namespace FileSys {
40class RegisteredCacheUnion; 39class ContentProvider;
40class ManualContentProvider;
41class VfsFilesystem; 41class VfsFilesystem;
42} // namespace FileSys 42} // namespace FileSys
43 43
@@ -120,7 +120,6 @@ private:
120 void InitializeWidgets(); 120 void InitializeWidgets();
121 void InitializeDebugWidgets(); 121 void InitializeDebugWidgets();
122 void InitializeRecentFileMenuActions(); 122 void InitializeRecentFileMenuActions();
123 void InitializeHotkeys();
124 123
125 void SetDefaultUIGeometry(); 124 void SetDefaultUIGeometry();
126 void RestoreUIState(); 125 void RestoreUIState();
@@ -176,6 +175,7 @@ private slots:
176 /// Called whenever a user selects a game in the game list widget. 175 /// Called whenever a user selects a game in the game list widget.
177 void OnGameListLoadFile(QString game_path); 176 void OnGameListLoadFile(QString game_path);
178 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); 177 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
178 void OnTransferableShaderCacheOpenFile(u64 program_id);
179 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); 179 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
180 void OnGameListCopyTID(u64 program_id); 180 void OnGameListCopyTID(u64 program_id);
181 void OnGameListNavigateToGamedbEntry(u64 program_id, 181 void OnGameListNavigateToGamedbEntry(u64 program_id,
@@ -195,6 +195,7 @@ private slots:
195 void OnAbout(); 195 void OnAbout();
196 void OnToggleFilterBar(); 196 void OnToggleFilterBar();
197 void OnDisplayTitleBars(bool); 197 void OnDisplayTitleBars(bool);
198 void InitializeHotkeys();
198 void ToggleFullscreen(); 199 void ToggleFullscreen();
199 void ShowFullscreen(); 200 void ShowFullscreen();
200 void HideFullscreen(); 201 void HideFullscreen();
@@ -204,7 +205,7 @@ private slots:
204 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 205 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
205 206
206private: 207private:
207 std::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, u64 program_id); 208 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
208 void UpdateStatusBar(); 209 void UpdateStatusBar();
209 210
210 Ui::MainWindow ui; 211 Ui::MainWindow ui;
@@ -232,12 +233,12 @@ private:
232 233
233 // FS 234 // FS
234 std::shared_ptr<FileSys::VfsFilesystem> vfs; 235 std::shared_ptr<FileSys::VfsFilesystem> vfs;
236 std::unique_ptr<FileSys::ManualContentProvider> provider;
235 237
236 // Debugger panes 238 // Debugger panes
237 ProfilerWidget* profilerWidget; 239 ProfilerWidget* profilerWidget;
238 MicroProfileDialog* microProfileDialog; 240 MicroProfileDialog* microProfileDialog;
239 GraphicsBreakPointsWidget* graphicsBreakpointsWidget; 241 GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
240 GraphicsSurfaceWidget* graphicsSurfaceWidget;
241 WaitTreeWidget* waitTreeWidget; 242 WaitTreeWidget* waitTreeWidget;
242 243
243 QAction* actions_recent_files[max_recent_files_item]; 244 QAction* actions_recent_files[max_recent_files_item];
@@ -251,4 +252,8 @@ protected:
251 void dropEvent(QDropEvent* event) override; 252 void dropEvent(QDropEvent* event) override;
252 void dragEnterEvent(QDragEnterEvent* event) override; 253 void dragEnterEvent(QDragEnterEvent* event) override;
253 void dragMoveEvent(QDragMoveEvent* event) override; 254 void dragMoveEvent(QDragMoveEvent* event) override;
255
256 // Overrides used to forward signals to the render window when the focus moves out.
257 void keyPressEvent(QKeyEvent* event) override;
258 void keyReleaseEvent(QKeyEvent* event) override;
254}; 259};
diff --git a/src/yuzu/ui_settings.cpp b/src/yuzu/ui_settings.cpp
index a314493fc..4bdc302e0 100644
--- a/src/yuzu/ui_settings.cpp
+++ b/src/yuzu/ui_settings.cpp
@@ -12,5 +12,4 @@ const Themes themes{{
12}}; 12}};
13 13
14Values values = {}; 14Values values = {};
15
16} // namespace UISettings 15} // namespace UISettings
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 82aaeedb0..dbd318e20 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -15,7 +15,12 @@
15namespace UISettings { 15namespace UISettings {
16 16
17using ContextualShortcut = std::pair<QString, int>; 17using ContextualShortcut = std::pair<QString, int>;
18using Shortcut = std::pair<QString, ContextualShortcut>; 18
19struct Shortcut {
20 QString name;
21 QString group;
22 ContextualShortcut shortcut;
23};
19 24
20using Themes = std::array<std::pair<const char*, const char*>, 2>; 25using Themes = std::array<std::pair<const char*, const char*>, 2>;
21extern const Themes themes; 26extern const Themes themes;
@@ -50,8 +55,8 @@ struct Values {
50 QString roms_path; 55 QString roms_path;
51 QString symbols_path; 56 QString symbols_path;
52 QString screenshot_path; 57 QString screenshot_path;
53 QString gamedir; 58 QString game_directory_path;
54 bool gamedir_deepscan; 59 bool game_directory_deepscan;
55 QStringList recent_files; 60 QStringList recent_files;
56 61
57 QString theme; 62 QString theme;
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
new file mode 100644
index 000000000..d3edf6ec3
--- /dev/null
+++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
@@ -0,0 +1,37 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QDialogButtonBox>
6#include <QKeySequenceEdit>
7#include <QVBoxLayout>
8#include "yuzu/util/sequence_dialog/sequence_dialog.h"
9
10SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
11 setWindowTitle(tr("Enter a hotkey"));
12 auto* layout = new QVBoxLayout(this);
13 key_sequence = new QKeySequenceEdit;
14 layout->addWidget(key_sequence);
15 auto* buttons =
16 new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal);
17 buttons->setCenterButtons(true);
18 layout->addWidget(buttons);
19 connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
20 connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
21 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
22}
23
24SequenceDialog::~SequenceDialog() = default;
25
26QKeySequence SequenceDialog::GetSequence() const {
27 // Only the first key is returned. The other 3, if present, are ignored.
28 return QKeySequence(key_sequence->keySequence()[0]);
29}
30
31bool SequenceDialog::focusNextPrevChild(bool next) {
32 return false;
33}
34
35void SequenceDialog::closeEvent(QCloseEvent*) {
36 reject();
37}
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.h b/src/yuzu/util/sequence_dialog/sequence_dialog.h
new file mode 100644
index 000000000..969c77740
--- /dev/null
+++ b/src/yuzu/util/sequence_dialog/sequence_dialog.h
@@ -0,0 +1,24 @@
1// Copyright 2018 Citra 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 <QDialog>
8
9class QKeySequenceEdit;
10
11class SequenceDialog : public QDialog {
12 Q_OBJECT
13
14public:
15 explicit SequenceDialog(QWidget* parent = nullptr);
16 ~SequenceDialog() override;
17
18 QKeySequence GetSequence() const;
19 void closeEvent(QCloseEvent*) override;
20
21private:
22 QKeySequenceEdit* key_sequence;
23 bool focusNextPrevChild(bool next) override;
24};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index de7a26e14..68a176032 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -19,6 +19,37 @@
19#include "input_common/sdl/sdl.h" 19#include "input_common/sdl/sdl.h"
20#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 20#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
21 21
22class SDLGLContext : public Core::Frontend::GraphicsContext {
23public:
24 explicit SDLGLContext() {
25 // create a hidden window to make the shared context against
26 window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position
27 SDL_WINDOWPOS_UNDEFINED, // y position
28 Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
29 SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
30 context = SDL_GL_CreateContext(window);
31 }
32
33 ~SDLGLContext() {
34 SDL_GL_DeleteContext(context);
35 SDL_DestroyWindow(window);
36 }
37
38 void MakeCurrent() override {
39 SDL_GL_MakeCurrent(window, context);
40 }
41
42 void DoneCurrent() override {
43 SDL_GL_MakeCurrent(window, nullptr);
44 }
45
46 void SwapBuffers() override {}
47
48private:
49 SDL_Window* window;
50 SDL_GLContext context;
51};
52
22void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 53void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
23 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); 54 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
24 InputCommon::GetMotionEmu()->Tilt(x, y); 55 InputCommon::GetMotionEmu()->Tilt(x, y);
@@ -153,6 +184,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
153 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); 184 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
154 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); 185 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
155 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); 186 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
187 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
156 188
157 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, 189 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
158 Common::g_scm_branch, Common::g_scm_desc); 190 Common::g_scm_branch, Common::g_scm_desc);
@@ -171,7 +203,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
171 if (fullscreen) { 203 if (fullscreen) {
172 Fullscreen(); 204 Fullscreen();
173 } 205 }
174
175 gl_context = SDL_GL_CreateContext(render_window); 206 gl_context = SDL_GL_CreateContext(render_window);
176 207
177 if (gl_context == nullptr) { 208 if (gl_context == nullptr) {
@@ -278,3 +309,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
278 309
279 SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); 310 SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
280} 311}
312
313std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const {
314 return std::make_unique<SDLGLContext>();
315}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index b0d4116cc..17e98227f 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -27,6 +27,8 @@ public:
27 /// Releases the GL context from the caller thread 27 /// Releases the GL context from the caller thread
28 void DoneCurrent() override; 28 void DoneCurrent() override;
29 29
30 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
31
30 /// Whether the window is still open, and a close request hasn't yet been sent 32 /// Whether the window is still open, and a close request hasn't yet been sent
31 bool IsOpen() const; 33 bool IsOpen() const;
32 34
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 245f25847..7ea4a1b18 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -33,6 +33,7 @@
33#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 33#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
34 34
35#include <getopt.h> 35#include <getopt.h>
36#include "core/file_sys/registered_cache.h"
36#ifndef _MSC_VER 37#ifndef _MSC_VER
37#include <unistd.h> 38#include <unistd.h>
38#endif 39#endif
@@ -178,6 +179,7 @@ int main(int argc, char** argv) {
178 } 179 }
179 180
180 Core::System& system{Core::System::GetInstance()}; 181 Core::System& system{Core::System::GetInstance()};
182 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
181 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 183 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
182 Service::FileSystem::CreateFactories(*system.GetFilesystem()); 184 Service::FileSystem::CreateFactories(*system.GetFilesystem());
183 185