summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt61
-rw-r--r--src/audio_core/stream.cpp2
-rw-r--r--src/common/CMakeLists.txt10
-rw-r--r--src/common/bit_field.h12
-rw-r--r--src/common/bit_util.h39
-rw-r--r--src/common/common_types.h7
-rw-r--r--src/common/detached_tasks.cpp8
-rw-r--r--src/common/logging/backend.cpp6
-rw-r--r--src/common/lz4_compression.cpp76
-rw-r--r--src/common/lz4_compression.h55
-rw-r--r--src/common/memory_hook.cpp (renamed from src/core/memory_hook.cpp)6
-rw-r--r--src/common/memory_hook.h (renamed from src/core/memory_hook.h)4
-rw-r--r--src/common/multi_level_queue.h337
-rw-r--r--src/common/page_table.cpp31
-rw-r--r--src/common/page_table.h84
-rw-r--r--src/common/swap.h174
-rw-r--r--src/common/thread.cpp37
-rw-r--r--src/common/thread.h14
-rw-r--r--src/common/thread_queue_list.h6
-rw-r--r--src/common/threadsafe_queue.h4
-rw-r--r--src/common/uint128.cpp45
-rw-r--r--src/common/uint128.h19
-rw-r--r--src/core/CMakeLists.txt14
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h8
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h2
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/core.h4
-rw-r--r--src/core/core_cpu.cpp6
-rw-r--r--src/core/core_timing.cpp2
-rw-r--r--src/core/core_timing.h2
-rw-r--r--src/core/core_timing_util.cpp6
-rw-r--r--src/core/core_timing_util.h3
-rw-r--r--src/core/file_sys/cheat_engine.cpp492
-rw-r--r--src/core/file_sys/cheat_engine.h234
-rw-r--r--src/core/file_sys/content_archive.h15
-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/errors.h3
-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.cpp79
-rw-r--r--src/core/file_sys/patch_manager.h9
-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.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/system_archive/system_archive.cpp3
-rw-r--r--src/core/file_sys/system_archive/system_version.cpp52
-rw-r--r--src/core/file_sys/system_archive/system_version.h16
-rw-r--r--src/core/frontend/emu_window.cpp6
-rw-r--r--src/core/hle/ipc.h44
-rw-r--r--src/core/hle/ipc_helpers.h30
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp6
-rw-r--r--src/core/hle/kernel/code_set.cpp12
-rw-r--r--src/core/hle/kernel/code_set.h89
-rw-r--r--src/core/hle/kernel/kernel.cpp13
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/mutex.cpp35
-rw-r--r--src/core/hle/kernel/mutex.h20
-rw-r--r--src/core/hle/kernel/object.cpp2
-rw-r--r--src/core/hle/kernel/object.h2
-rw-r--r--src/core/hle/kernel/process.cpp51
-rw-r--r--src/core/hle/kernel/process.h95
-rw-r--r--src/core/hle/kernel/readable_event.cpp2
-rw-r--r--src/core/hle/kernel/readable_event.h2
-rw-r--r--src/core/hle/kernel/resource_limit.cpp7
-rw-r--r--src/core/hle/kernel/resource_limit.h11
-rw-r--r--src/core/hle/kernel/scheduler.cpp68
-rw-r--r--src/core/hle/kernel/scheduler.h6
-rw-r--r--src/core/hle/kernel/server_port.cpp2
-rw-r--r--src/core/hle/kernel/server_port.h2
-rw-r--r--src/core/hle/kernel/server_session.cpp2
-rw-r--r--src/core/hle/kernel/server_session.h2
-rw-r--r--src/core/hle/kernel/shared_memory.cpp11
-rw-r--r--src/core/hle/kernel/shared_memory.h10
-rw-r--r--src/core/hle/kernel/svc.cpp302
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
-rw-r--r--src/core/hle/kernel/thread.cpp93
-rw-r--r--src/core/hle/kernel/thread.h32
-rw-r--r--src/core/hle/kernel/transfer_memory.cpp81
-rw-r--r--src/core/hle/kernel/transfer_memory.h103
-rw-r--r--src/core/hle/kernel/vm_manager.cpp102
-rw-r--r--src/core/hle/kernel/vm_manager.h69
-rw-r--r--src/core/hle/kernel/wait_object.h2
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/am/am.cpp185
-rw-r--r--src/core/hle/service/am/am.h31
-rw-r--r--src/core/hle/service/audio/audin_u.cpp4
-rw-r--r--src/core/hle/service/audio/audout_u.cpp4
-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/audio/hwopus.cpp82
-rw-r--r--src/core/hle/service/fatal/fatal.cpp89
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp19
-rw-r--r--src/core/hle/service/filesystem/filesystem.h2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp94
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h30
-rw-r--r--src/core/hle/service/hid/controllers/npad.h102
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h4
-rw-r--r--src/core/hle/service/hid/hid.cpp10
-rw-r--r--src/core/hle/service/hid/hid.h5
-rw-r--r--src/core/hle/service/ldr/ldr.cpp8
-rw-r--r--src/core/hle/service/lm/lm.cpp2
-rw-r--r--src/core/hle/service/nfc/nfc.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp17
-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.cpp4
-rw-r--r--src/core/hle/service/service.h2
-rw-r--r--src/core/hle/service/set/set_cal.h2
-rw-r--r--src/core/hle/service/set/set_sys.cpp79
-rw-r--r--src/core/hle/service/set/set_sys.h2
-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.cpp10
-rw-r--r--src/core/hle/service/vi/vi.cpp5
-rw-r--r--src/core/loader/elf.cpp3
-rw-r--r--src/core/loader/linker.cpp147
-rw-r--r--src/core/loader/linker.h36
-rw-r--r--src/core/loader/nro.cpp3
-rw-r--r--src/core/loader/nro.h4
-rw-r--r--src/core/loader/nso.cpp117
-rw-r--r--src/core/loader/nso.h43
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/memory.cpp218
-rw-r--r--src/core/memory.h83
-rw-r--r--src/core/memory_setup.h19
-rw-r--r--src/core/perf_stats.cpp10
-rw-r--r--src/core/settings.cpp1
-rw-r--r--src/core/settings.h1
-rw-r--r--src/input_common/keyboard.cpp8
-rw-r--r--src/input_common/motion_emu.cpp10
-rw-r--r--src/input_common/sdl/sdl.h12
-rw-r--r--src/input_common/sdl/sdl_impl.cpp40
-rw-r--r--src/input_common/sdl/sdl_impl.h5
-rw-r--r--src/tests/CMakeLists.txt3
-rw-r--r--src/tests/common/bit_field.cpp90
-rw-r--r--src/tests/common/bit_utils.cpp23
-rw-r--r--src/tests/common/multi_level_queue.cpp55
-rw-r--r--src/tests/core/arm/arm_test_common.cpp3
-rw-r--r--src/tests/core/arm/arm_test_common.h8
-rw-r--r--src/video_core/CMakeLists.txt6
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp4
-rw-r--r--src/video_core/debug_utils/debug_utils.h4
-rw-r--r--src/video_core/dma_pusher.cpp7
-rw-r--r--src/video_core/dma_pusher.h1
-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.cpp19
-rw-r--r--src/video_core/engines/kepler_memory.h7
-rw-r--r--src/video_core/engines/maxwell_3d.cpp49
-rw-r--r--src/video_core/engines/maxwell_3d.h8
-rw-r--r--src/video_core/engines/maxwell_dma.cpp41
-rw-r--r--src/video_core/engines/maxwell_dma.h9
-rw-r--r--src/video_core/gpu.cpp15
-rw-r--r--src/video_core/gpu.h23
-rw-r--r--src/video_core/gpu_asynch.cpp8
-rw-r--r--src/video_core/gpu_asynch.h6
-rw-r--r--src/video_core/gpu_synch.cpp6
-rw-r--r--src/video_core/gpu_synch.h6
-rw-r--r--src/video_core/gpu_thread.cpp155
-rw-r--r--src/video_core/gpu_thread.h136
-rw-r--r--src/video_core/macro_interpreter.cpp20
-rw-r--r--src/video_core/memory_manager.cpp490
-rw-r--r--src/video_core/memory_manager.h164
-rw-r--r--src/video_core/morton.cpp15
-rw-r--r--src/video_core/morton.h2
-rw-r--r--src/video_core/rasterizer_cache.h74
-rw-r--r--src/video_core/rasterizer_interface.h9
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp31
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h33
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.cpp42
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.h17
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.cpp12
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.h4
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp36
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h17
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp97
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h41
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp64
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h24
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp36
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp46
-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.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h11
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp31
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h34
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_manager.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_manager.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp210
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h92
-rw-r--r--src/video_core/textures/decoders.cpp32
-rw-r--r--src/video_core/textures/decoders.h13
-rw-r--r--src/web_service/web_backend.cpp4
-rw-r--r--src/yuzu/applets/profile_select.cpp7
-rw-r--r--src/yuzu/applets/software_keyboard.cpp18
-rw-r--r--src/yuzu/applets/web_browser.cpp4
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/configuration/config.cpp446
-rw-r--r--src/yuzu/configuration/config.h5
-rw-r--r--src/yuzu/configuration/configure_general.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.ui20
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp213
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.h2
-rw-r--r--src/yuzu/debugger/profiler.cpp1
-rw-r--r--src/yuzu/debugger/profiler.h9
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/game_list.cpp4
-rw-r--r--src/yuzu/game_list.h1
-rw-r--r--src/yuzu/main.cpp48
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu_cmd/config.cpp1
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
226 files changed, 5781 insertions, 2508 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f69d00a2b..6c99dd5e2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,18 +1,79 @@
1# Enable modules to include each other's files 1# Enable modules to include each other's files
2include_directories(.) 2include_directories(.)
3 3
4# CMake seems to only define _DEBUG on Windows
5set_property(DIRECTORY APPEND PROPERTY
6 COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
7
8# Set compilation flags
9if (MSVC)
10 set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
11
12 # Silence "deprecation" warnings
13 add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS)
14
15 # Avoid windows.h junk
16 add_definitions(-DNOMINMAX)
17
18 # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
19 add_definitions(-DWIN32_LEAN_AND_MEAN)
20
21 # /W3 - Level 3 warnings
22 # /MP - Multi-threaded compilation
23 # /Zi - Output debugging information
24 # /Zo - enhanced debug info for optimized builds
25 # /permissive- - enables stricter C++ standards conformance checks
26 # /EHsc - C++-only exception handling semantics
27 # /Zc:throwingNew - let codegen assume `operator new` will never return null
28 # /Zc:inline - let codegen omit inline functions in object files
29 add_compile_options(/W3 /MP /Zi /Zo /permissive- /EHsc /std:c++latest /Zc:throwingNew,inline)
30
31 # /GS- - No stack buffer overflow checks
32 add_compile_options("$<$<CONFIG:Release>:/GS->")
33
34 set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE)
35 set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
36else()
37 add_compile_options("-Wno-attributes")
38
39 if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
40 add_compile_options("-stdlib=libc++")
41 endif()
42
43 # Set file offset size to 64 bits.
44 #
45 # On modern Unixes, this is typically already the case. The lone exception is
46 # glibc, which may default to 32 bits. glibc allows this to be configured
47 # by setting _FILE_OFFSET_BITS.
48 if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
49 add_definitions(-D_FILE_OFFSET_BITS=64)
50 endif()
51
52 if (MINGW)
53 add_definitions(-DMINGW_HAS_SECURE_API)
54
55 if (MINGW_STATIC_BUILD)
56 add_definitions(-DQT_STATICPLUGIN)
57 add_compile_options("-static")
58 endif()
59 endif()
60endif()
61
4add_subdirectory(common) 62add_subdirectory(common)
5add_subdirectory(core) 63add_subdirectory(core)
6add_subdirectory(audio_core) 64add_subdirectory(audio_core)
7add_subdirectory(video_core) 65add_subdirectory(video_core)
8add_subdirectory(input_common) 66add_subdirectory(input_common)
9add_subdirectory(tests) 67add_subdirectory(tests)
68
10if (ENABLE_SDL2) 69if (ENABLE_SDL2)
11 add_subdirectory(yuzu_cmd) 70 add_subdirectory(yuzu_cmd)
12endif() 71endif()
72
13if (ENABLE_QT) 73if (ENABLE_QT)
14 add_subdirectory(yuzu) 74 add_subdirectory(yuzu)
15endif() 75endif()
76
16if (ENABLE_WEB_SERVICE) 77if (ENABLE_WEB_SERVICE)
17 add_subdirectory(web_service) 78 add_subdirectory(web_service)
18endif() 79endif()
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 4b66a6786..22a3f8c84 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -38,7 +38,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
38 sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { 38 sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
39 39
40 release_event = core_timing.RegisterEvent( 40 release_event = core_timing.RegisterEvent(
41 name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); 41 name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); });
42} 42}
43 43
44void Stream::Play() { 44void Stream::Play() {
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 3d30f0e3e..5639021d3 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -91,11 +91,18 @@ 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
97 memory_hook.cpp
98 memory_hook.h
95 microprofile.cpp 99 microprofile.cpp
96 microprofile.h 100 microprofile.h
97 microprofileui.h 101 microprofileui.h
98 misc.cpp 102 misc.cpp
103 multi_level_queue.h
104 page_table.cpp
105 page_table.h
99 param_package.cpp 106 param_package.cpp
100 param_package.h 107 param_package.h
101 quaternion.h 108 quaternion.h
@@ -114,6 +121,8 @@ add_library(common STATIC
114 threadsafe_queue.h 121 threadsafe_queue.h
115 timer.cpp 122 timer.cpp
116 timer.h 123 timer.h
124 uint128.cpp
125 uint128.h
117 vector_math.h 126 vector_math.h
118 web_result.h 127 web_result.h
119) 128)
@@ -129,3 +138,4 @@ endif()
129create_target_directory_groups(common) 138create_target_directory_groups(common)
130 139
131target_link_libraries(common PUBLIC Boost::boost fmt microprofile) 140target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
141target_link_libraries(common PRIVATE lz4_static)
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 7433c39ba..902e668e3 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -34,6 +34,7 @@
34#include <limits> 34#include <limits>
35#include <type_traits> 35#include <type_traits>
36#include "common/common_funcs.h" 36#include "common/common_funcs.h"
37#include "common/swap.h"
37 38
38/* 39/*
39 * Abstract bitfield class 40 * Abstract bitfield class
@@ -108,7 +109,7 @@
108 * symptoms. 109 * symptoms.
109 */ 110 */
110#pragma pack(1) 111#pragma pack(1)
111template <std::size_t Position, std::size_t Bits, typename T> 112template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag>
112struct BitField { 113struct BitField {
113private: 114private:
114 // UnderlyingType is T for non-enum types and the underlying type of T if 115 // UnderlyingType is T for non-enum types and the underlying type of T if
@@ -121,6 +122,8 @@ private:
121 // We store the value as the unsigned type to avoid undefined behaviour on value shifting 122 // We store the value as the unsigned type to avoid undefined behaviour on value shifting
122 using StorageType = std::make_unsigned_t<UnderlyingType>; 123 using StorageType = std::make_unsigned_t<UnderlyingType>;
123 124
125 using StorageTypeWithEndian = typename AddEndian<StorageType, EndianTag>::type;
126
124public: 127public:
125 /// Constants to allow limited introspection of fields if needed 128 /// Constants to allow limited introspection of fields if needed
126 static constexpr std::size_t position = Position; 129 static constexpr std::size_t position = Position;
@@ -170,7 +173,7 @@ public:
170 } 173 }
171 174
172 constexpr FORCE_INLINE void Assign(const T& value) { 175 constexpr FORCE_INLINE void Assign(const T& value) {
173 storage = (storage & ~mask) | FormatValue(value); 176 storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
174 } 177 }
175 178
176 constexpr T Value() const { 179 constexpr T Value() const {
@@ -182,7 +185,7 @@ public:
182 } 185 }
183 186
184private: 187private:
185 StorageType storage; 188 StorageTypeWithEndian storage;
186 189
187 static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); 190 static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
188 191
@@ -193,3 +196,6 @@ private:
193 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField"); 196 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField");
194}; 197};
195#pragma pack() 198#pragma pack()
199
200template <std::size_t Position, std::size_t Bits, typename T>
201using BitFieldBE = BitField<Position, Bits, T, BETag>;
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index 1eea17ba1..a4f9ed4aa 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -58,4 +58,43 @@ inline u64 CountLeadingZeroes64(u64 value) {
58 return __builtin_clzll(value); 58 return __builtin_clzll(value);
59} 59}
60#endif 60#endif
61
62#ifdef _MSC_VER
63inline u32 CountTrailingZeroes32(u32 value) {
64 unsigned long trailing_zero = 0;
65
66 if (_BitScanForward(&trailing_zero, value) != 0) {
67 return trailing_zero;
68 }
69
70 return 32;
71}
72
73inline u64 CountTrailingZeroes64(u64 value) {
74 unsigned long trailing_zero = 0;
75
76 if (_BitScanForward64(&trailing_zero, value) != 0) {
77 return trailing_zero;
78 }
79
80 return 64;
81}
82#else
83inline u32 CountTrailingZeroes32(u32 value) {
84 if (value == 0) {
85 return 32;
86 }
87
88 return __builtin_ctz(value);
89}
90
91inline u64 CountTrailingZeroes64(u64 value) {
92 if (value == 0) {
93 return 64;
94 }
95
96 return __builtin_ctzll(value);
97}
98#endif
99
61} // namespace Common 100} // namespace Common
diff --git a/src/common/common_types.h b/src/common/common_types.h
index 6b1766dca..4cec89fbd 100644
--- a/src/common/common_types.h
+++ b/src/common/common_types.h
@@ -40,10 +40,9 @@ using s64 = std::int64_t; ///< 64-bit signed int
40using f32 = float; ///< 32-bit floating point 40using f32 = float; ///< 32-bit floating point
41using f64 = double; ///< 64-bit floating point 41using f64 = double; ///< 64-bit floating point
42 42
43// TODO: It would be nice to eventually replace these with strong types that prevent accidental 43using VAddr = u64; ///< Represents a pointer in the userspace virtual address space.
44// conversion between each other. 44using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
45using VAddr = u64; ///< Represents a pointer in the userspace virtual address space. 45using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space.
46using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
47 46
48using u128 = std::array<std::uint64_t, 2>; 47using u128 = std::array<std::uint64_t, 2>;
49static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); 48static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index a347d9e02..f268d6021 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -16,22 +16,22 @@ DetachedTasks::DetachedTasks() {
16} 16}
17 17
18void DetachedTasks::WaitForAllTasks() { 18void DetachedTasks::WaitForAllTasks() {
19 std::unique_lock<std::mutex> lock(mutex); 19 std::unique_lock lock{mutex};
20 cv.wait(lock, [this]() { return count == 0; }); 20 cv.wait(lock, [this]() { return count == 0; });
21} 21}
22 22
23DetachedTasks::~DetachedTasks() { 23DetachedTasks::~DetachedTasks() {
24 std::unique_lock<std::mutex> lock(mutex); 24 std::unique_lock lock{mutex};
25 ASSERT(count == 0); 25 ASSERT(count == 0);
26 instance = nullptr; 26 instance = nullptr;
27} 27}
28 28
29void DetachedTasks::AddTask(std::function<void()> task) { 29void DetachedTasks::AddTask(std::function<void()> task) {
30 std::unique_lock<std::mutex> lock(instance->mutex); 30 std::unique_lock lock{instance->mutex};
31 ++instance->count; 31 ++instance->count;
32 std::thread([task{std::move(task)}]() { 32 std::thread([task{std::move(task)}]() {
33 task(); 33 task();
34 std::unique_lock<std::mutex> lock(instance->mutex); 34 std::unique_lock lock{instance->mutex};
35 --instance->count; 35 --instance->count;
36 std::notify_all_at_thread_exit(instance->cv, std::move(lock)); 36 std::notify_all_at_thread_exit(instance->cv, std::move(lock));
37 }) 37 })
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 4462ff3fb..a03179520 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -46,12 +46,12 @@ public:
46 } 46 }
47 47
48 void AddBackend(std::unique_ptr<Backend> backend) { 48 void AddBackend(std::unique_ptr<Backend> backend) {
49 std::lock_guard<std::mutex> lock(writing_mutex); 49 std::lock_guard lock{writing_mutex};
50 backends.push_back(std::move(backend)); 50 backends.push_back(std::move(backend));
51 } 51 }
52 52
53 void RemoveBackend(std::string_view backend_name) { 53 void RemoveBackend(std::string_view backend_name) {
54 std::lock_guard<std::mutex> lock(writing_mutex); 54 std::lock_guard lock{writing_mutex};
55 const auto it = 55 const auto it =
56 std::remove_if(backends.begin(), backends.end(), 56 std::remove_if(backends.begin(), backends.end(),
57 [&backend_name](const auto& i) { return backend_name == i->GetName(); }); 57 [&backend_name](const auto& i) { return backend_name == i->GetName(); });
@@ -80,7 +80,7 @@ private:
80 backend_thread = std::thread([&] { 80 backend_thread = std::thread([&] {
81 Entry entry; 81 Entry entry;
82 auto write_logs = [&](Entry& e) { 82 auto write_logs = [&](Entry& e) {
83 std::lock_guard<std::mutex> lock(writing_mutex); 83 std::lock_guard lock{writing_mutex};
84 for (const auto& backend : backends) { 84 for (const auto& backend : backends) {
85 backend->Write(e); 85 backend->Write(e);
86 } 86 }
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..fe2231a6c
--- /dev/null
+++ b/src/common/lz4_compression.h
@@ -0,0 +1,55 @@
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 <vector>
6
7#include "common/common_types.h"
8
9namespace Common::Compression {
10
11/**
12 * Compresses a source memory region with LZ4 and returns the compressed data in a vector.
13 *
14 * @param source the uncompressed source memory region.
15 * @param source_size the size in bytes of the uncompressed source memory region.
16 *
17 * @return the compressed data.
18 */
19std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
20
21/**
22 * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression
23 * levels result in a smaller compressed size, but require more CPU time for compression. The
24 * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can
25 * also be decompressed with the default LZ4 decompression.
26 *
27 * @param source the uncompressed source memory region.
28 * @param source_size the size in bytes of the uncompressed source memory region.
29 * @param compression_level the used compression level. Should be between 3 and 12.
30 *
31 * @return the compressed data.
32 */
33std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level);
34
35/**
36 * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
37 *
38 * @param source the uncompressed source memory region.
39 * @param source_size the size in bytes of the uncompressed source memory region.
40 *
41 * @return the compressed data.
42 */
43std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
44
45/**
46 * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector.
47 *
48 * @param compressed the compressed source memory region.
49 * @param uncompressed_size the size in bytes of the uncompressed data.
50 *
51 * @return the decompressed data.
52 */
53std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size);
54
55} // namespace Common::Compression \ No newline at end of file
diff --git a/src/core/memory_hook.cpp b/src/common/memory_hook.cpp
index c61c6c1fb..3986986d6 100644
--- a/src/core/memory_hook.cpp
+++ b/src/common/memory_hook.cpp
@@ -2,10 +2,10 @@
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/memory_hook.h" 5#include "common/memory_hook.h"
6 6
7namespace Memory { 7namespace Common {
8 8
9MemoryHook::~MemoryHook() = default; 9MemoryHook::~MemoryHook() = default;
10 10
11} // namespace Memory 11} // namespace Common
diff --git a/src/core/memory_hook.h b/src/common/memory_hook.h
index 940777107..adaa4c2c5 100644
--- a/src/core/memory_hook.h
+++ b/src/common/memory_hook.h
@@ -9,7 +9,7 @@
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace Memory { 12namespace Common {
13 13
14/** 14/**
15 * Memory hooks have two purposes: 15 * Memory hooks have two purposes:
@@ -44,4 +44,4 @@ public:
44}; 44};
45 45
46using MemoryHookPointer = std::shared_ptr<MemoryHook>; 46using MemoryHookPointer = std::shared_ptr<MemoryHook>;
47} // namespace Memory 47} // namespace Common
diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h
new file mode 100644
index 000000000..2b61b91e0
--- /dev/null
+++ b/src/common/multi_level_queue.h
@@ -0,0 +1,337 @@
1// Copyright 2019 TuxSH
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 <iterator>
9#include <list>
10#include <utility>
11
12#include "common/bit_util.h"
13#include "common/common_types.h"
14
15namespace Common {
16
17/**
18 * A MultiLevelQueue is a type of priority queue which has the following characteristics:
19 * - iteratable through each of its elements.
20 * - back can be obtained.
21 * - O(1) add, lookup (both front and back)
22 * - discrete priorities and a max of 64 priorities (limited domain)
23 * This type of priority queue is normaly used for managing threads within an scheduler
24 */
25template <typename T, std::size_t Depth>
26class MultiLevelQueue {
27public:
28 using value_type = T;
29 using reference = value_type&;
30 using const_reference = const value_type&;
31 using pointer = value_type*;
32 using const_pointer = const value_type*;
33
34 using difference_type = typename std::pointer_traits<pointer>::difference_type;
35 using size_type = std::size_t;
36
37 template <bool is_constant>
38 class iterator_impl {
39 public:
40 using iterator_category = std::bidirectional_iterator_tag;
41 using value_type = T;
42 using pointer = std::conditional_t<is_constant, T*, const T*>;
43 using reference = std::conditional_t<is_constant, const T&, T&>;
44 using difference_type = typename std::pointer_traits<pointer>::difference_type;
45
46 friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
47 if (lhs.IsEnd() && rhs.IsEnd())
48 return true;
49 return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it);
50 }
51
52 friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
53 return !operator==(lhs, rhs);
54 }
55
56 reference operator*() const {
57 return *it;
58 }
59
60 pointer operator->() const {
61 return it.operator->();
62 }
63
64 iterator_impl& operator++() {
65 if (IsEnd()) {
66 return *this;
67 }
68
69 ++it;
70
71 if (it == GetEndItForPrio()) {
72 u64 prios = mlq.used_priorities;
73 prios &= ~((1ULL << (current_priority + 1)) - 1);
74 if (prios == 0) {
75 current_priority = mlq.depth();
76 } else {
77 current_priority = CountTrailingZeroes64(prios);
78 it = GetBeginItForPrio();
79 }
80 }
81 return *this;
82 }
83
84 iterator_impl& operator--() {
85 if (IsEnd()) {
86 if (mlq.used_priorities != 0) {
87 current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities);
88 it = GetEndItForPrio();
89 --it;
90 }
91 } else if (it == GetBeginItForPrio()) {
92 u64 prios = mlq.used_priorities;
93 prios &= (1ULL << current_priority) - 1;
94 if (prios != 0) {
95 current_priority = CountTrailingZeroes64(prios);
96 it = GetEndItForPrio();
97 --it;
98 }
99 } else {
100 --it;
101 }
102 return *this;
103 }
104
105 iterator_impl operator++(int) {
106 const iterator_impl v{*this};
107 ++(*this);
108 return v;
109 }
110
111 iterator_impl operator--(int) {
112 const iterator_impl v{*this};
113 --(*this);
114 return v;
115 }
116
117 // allow implicit const->non-const
118 iterator_impl(const iterator_impl<false>& other)
119 : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
120
121 iterator_impl(const iterator_impl<true>& other)
122 : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
123
124 iterator_impl& operator=(const iterator_impl<false>& other) {
125 mlq = other.mlq;
126 it = other.it;
127 current_priority = other.current_priority;
128 return *this;
129 }
130
131 friend class iterator_impl<true>;
132 iterator_impl() = default;
133
134 private:
135 friend class MultiLevelQueue;
136 using container_ref =
137 std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>;
138 using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator,
139 typename std::list<T>::iterator>;
140
141 explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority)
142 : mlq(mlq), it(it), current_priority(current_priority) {}
143 explicit iterator_impl(container_ref mlq, u32 current_priority)
144 : mlq(mlq), it(), current_priority(current_priority) {}
145
146 bool IsEnd() const {
147 return current_priority == mlq.depth();
148 }
149
150 list_iterator GetBeginItForPrio() const {
151 return mlq.levels[current_priority].begin();
152 }
153
154 list_iterator GetEndItForPrio() const {
155 return mlq.levels[current_priority].end();
156 }
157
158 container_ref mlq;
159 list_iterator it;
160 u32 current_priority;
161 };
162
163 using iterator = iterator_impl<false>;
164 using const_iterator = iterator_impl<true>;
165
166 void add(const T& element, u32 priority, bool send_back = true) {
167 if (send_back)
168 levels[priority].push_back(element);
169 else
170 levels[priority].push_front(element);
171 used_priorities |= 1ULL << priority;
172 }
173
174 void remove(const T& element, u32 priority) {
175 auto it = ListIterateTo(levels[priority], element);
176 if (it == levels[priority].end())
177 return;
178 levels[priority].erase(it);
179 if (levels[priority].empty()) {
180 used_priorities &= ~(1ULL << priority);
181 }
182 }
183
184 void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) {
185 remove(element, old_priority);
186 add(element, new_priority, !adjust_front);
187 }
188 void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) {
189 adjust(*it, old_priority, new_priority, adjust_front);
190 }
191
192 void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) {
193 ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority],
194 ListIterateTo(levels[priority], element));
195
196 other.used_priorities |= 1ULL << priority;
197
198 if (levels[priority].empty()) {
199 used_priorities &= ~(1ULL << priority);
200 }
201 }
202
203 void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) {
204 transfer_to_front(*it, priority, other);
205 }
206
207 void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) {
208 ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority],
209 ListIterateTo(levels[priority], element));
210
211 other.used_priorities |= 1ULL << priority;
212
213 if (levels[priority].empty()) {
214 used_priorities &= ~(1ULL << priority);
215 }
216 }
217
218 void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) {
219 transfer_to_back(*it, priority, other);
220 }
221
222 void yield(u32 priority, std::size_t n = 1) {
223 ListShiftForward(levels[priority], n);
224 }
225
226 std::size_t depth() const {
227 return Depth;
228 }
229
230 std::size_t size(u32 priority) const {
231 return levels[priority].size();
232 }
233
234 std::size_t size() const {
235 u64 priorities = used_priorities;
236 std::size_t size = 0;
237 while (priorities != 0) {
238 const u64 current_priority = CountTrailingZeroes64(priorities);
239 size += levels[current_priority].size();
240 priorities &= ~(1ULL << current_priority);
241 }
242 return size;
243 }
244
245 bool empty() const {
246 return used_priorities == 0;
247 }
248
249 bool empty(u32 priority) const {
250 return (used_priorities & (1ULL << priority)) == 0;
251 }
252
253 u32 highest_priority_set(u32 max_priority = 0) const {
254 const u64 priorities =
255 max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
256 return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
257 }
258
259 u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
260 const u64 priorities = min_priority >= Depth - 1
261 ? used_priorities
262 : (used_priorities & ((1ULL << (min_priority + 1)) - 1));
263 return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
264 }
265
266 const_iterator cbegin(u32 max_prio = 0) const {
267 const u32 priority = highest_priority_set(max_prio);
268 return priority == Depth ? cend()
269 : const_iterator{*this, levels[priority].cbegin(), priority};
270 }
271 const_iterator begin(u32 max_prio = 0) const {
272 return cbegin(max_prio);
273 }
274 iterator begin(u32 max_prio = 0) {
275 const u32 priority = highest_priority_set(max_prio);
276 return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
277 }
278
279 const_iterator cend(u32 min_prio = Depth - 1) const {
280 return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
281 }
282 const_iterator end(u32 min_prio = Depth - 1) const {
283 return cend(min_prio);
284 }
285 iterator end(u32 min_prio = Depth - 1) {
286 return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
287 }
288
289 T& front(u32 max_priority = 0) {
290 const u32 priority = highest_priority_set(max_priority);
291 return levels[priority == Depth ? 0 : priority].front();
292 }
293 const T& front(u32 max_priority = 0) const {
294 const u32 priority = highest_priority_set(max_priority);
295 return levels[priority == Depth ? 0 : priority].front();
296 }
297
298 T back(u32 min_priority = Depth - 1) {
299 const u32 priority = lowest_priority_set(min_priority); // intended
300 return levels[priority == Depth ? 63 : priority].back();
301 }
302 const T& back(u32 min_priority = Depth - 1) const {
303 const u32 priority = lowest_priority_set(min_priority); // intended
304 return levels[priority == Depth ? 63 : priority].back();
305 }
306
307private:
308 using const_list_iterator = typename std::list<T>::const_iterator;
309
310 static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) {
311 if (shift >= list.size()) {
312 return;
313 }
314
315 const auto begin_range = list.begin();
316 const auto end_range = std::next(begin_range, shift);
317 list.splice(list.end(), list, begin_range, end_range);
318 }
319
320 static void ListSplice(std::list<T>& in_list, const_list_iterator position,
321 std::list<T>& out_list, const_list_iterator element) {
322 in_list.splice(position, out_list, element);
323 }
324
325 static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) {
326 auto it = list.cbegin();
327 while (it != list.cend() && *it != element) {
328 ++it;
329 }
330 return it;
331 }
332
333 std::array<std::list<T>, Depth> levels;
334 u64 used_priorities = 0;
335};
336
337} // namespace Common
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
new file mode 100644
index 000000000..69b7abc54
--- /dev/null
+++ b/src/common/page_table.cpp
@@ -0,0 +1,31 @@
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/page_table.h"
6
7namespace Common {
8
9PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {}
10
11PageTable::~PageTable() = default;
12
13void PageTable::Resize(std::size_t address_space_width_in_bits) {
14 const std::size_t num_page_table_entries = 1ULL
15 << (address_space_width_in_bits - page_size_in_bits);
16
17 pointers.resize(num_page_table_entries);
18 attributes.resize(num_page_table_entries);
19 backing_addr.resize(num_page_table_entries);
20
21 // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
22 // vector size is subsequently decreased (via resize), the vector might not automatically
23 // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
24 // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
25
26 pointers.shrink_to_fit();
27 attributes.shrink_to_fit();
28 backing_addr.shrink_to_fit();
29}
30
31} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
new file mode 100644
index 000000000..8b8ff0bb8
--- /dev/null
+++ b/src/common/page_table.h
@@ -0,0 +1,84 @@
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#include <boost/icl/interval_map.hpp>
9#include "common/common_types.h"
10#include "common/memory_hook.h"
11
12namespace Common {
13
14enum class PageType : u8 {
15 /// Page is unmapped and should cause an access error.
16 Unmapped,
17 /// Page is mapped to regular memory. This is the only type you can get pointers to.
18 Memory,
19 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
20 /// invalidation
21 RasterizerCachedMemory,
22 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
23 Special,
24 /// Page is allocated for use.
25 Allocated,
26};
27
28struct SpecialRegion {
29 enum class Type {
30 DebugHook,
31 IODevice,
32 } type;
33
34 MemoryHookPointer handler;
35
36 bool operator<(const SpecialRegion& other) const {
37 return std::tie(type, handler) < std::tie(other.type, other.handler);
38 }
39
40 bool operator==(const SpecialRegion& other) const {
41 return std::tie(type, handler) == std::tie(other.type, other.handler);
42 }
43};
44
45/**
46 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
47 * mimics the way a real CPU page table works.
48 */
49struct PageTable {
50 explicit PageTable(std::size_t page_size_in_bits);
51 ~PageTable();
52
53 /**
54 * Resizes the page table to be able to accomodate enough pages within
55 * a given address space.
56 *
57 * @param address_space_width_in_bits The address size width in bits.
58 */
59 void Resize(std::size_t address_space_width_in_bits);
60
61 /**
62 * Vector of memory pointers backing each page. An entry can only be non-null if the
63 * corresponding entry in the `attributes` vector is of type `Memory`.
64 */
65 std::vector<u8*> pointers;
66
67 /**
68 * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
69 * of type `Special`.
70 */
71 boost::icl::interval_map<u64, std::set<SpecialRegion>> special_regions;
72
73 /**
74 * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
75 * the corresponding entry in `pointers` MUST be set to null.
76 */
77 std::vector<PageType> attributes;
78
79 std::vector<u64> backing_addr;
80
81 const std::size_t page_size_in_bits{};
82};
83
84} // namespace Common
diff --git a/src/common/swap.h b/src/common/swap.h
index 0e219747f..b3eab1324 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -17,6 +17,8 @@
17 17
18#pragma once 18#pragma once
19 19
20#include <type_traits>
21
20#if defined(_MSC_VER) 22#if defined(_MSC_VER)
21#include <cstdlib> 23#include <cstdlib>
22#elif defined(__linux__) 24#elif defined(__linux__)
@@ -170,7 +172,7 @@ struct swap_struct_t {
170 using swapped_t = swap_struct_t; 172 using swapped_t = swap_struct_t;
171 173
172protected: 174protected:
173 T value = T(); 175 T value;
174 176
175 static T swap(T v) { 177 static T swap(T v) {
176 return F::swap(v); 178 return F::swap(v);
@@ -605,52 +607,154 @@ struct swap_double_t {
605 } 607 }
606}; 608};
607 609
608#if COMMON_LITTLE_ENDIAN 610template <typename T>
609using u16_le = u16; 611struct swap_enum_t {
610using u32_le = u32; 612 static_assert(std::is_enum_v<T>);
611using u64_le = u64; 613 using base = std::underlying_type_t<T>;
614
615public:
616 swap_enum_t() = default;
617 swap_enum_t(const T& v) : value(swap(v)) {}
618
619 swap_enum_t& operator=(const T& v) {
620 value = swap(v);
621 return *this;
622 }
623
624 operator T() const {
625 return swap(value);
626 }
627
628 explicit operator base() const {
629 return static_cast<base>(swap(value));
630 }
612 631
613using s16_le = s16; 632protected:
614using s32_le = s32; 633 T value{};
615using s64_le = s64; 634 // clang-format off
635 using swap_t = std::conditional_t<
636 std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t<
637 std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t<
638 std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t<
639 std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t<
640 std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t<
641 std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>;
642 // clang-format on
643 static T swap(T x) {
644 return static_cast<T>(swap_t::swap(static_cast<base>(x)));
645 }
646};
616 647
617using float_le = float; 648struct SwapTag {}; // Use the different endianness from the system
618using double_le = double; 649struct KeepTag {}; // Use the same endianness as the system
619 650
620using u64_be = swap_struct_t<u64, swap_64_t<u64>>; 651template <typename T, typename Tag>
621using s64_be = swap_struct_t<s64, swap_64_t<s64>>; 652struct AddEndian;
622 653
623using u32_be = swap_struct_t<u32, swap_32_t<u32>>; 654// KeepTag specializations
624using s32_be = swap_struct_t<s32, swap_32_t<s32>>;
625 655
626using u16_be = swap_struct_t<u16, swap_16_t<u16>>; 656template <typename T>
627using s16_be = swap_struct_t<s16, swap_16_t<s16>>; 657struct AddEndian<T, KeepTag> {
658 using type = T;
659};
628 660
629using float_be = swap_struct_t<float, swap_float_t<float>>; 661// SwapTag specializations
630using double_be = swap_struct_t<double, swap_double_t<double>>; 662
631#else 663template <>
664struct AddEndian<u8, SwapTag> {
665 using type = u8;
666};
667
668template <>
669struct AddEndian<u16, SwapTag> {
670 using type = swap_struct_t<u16, swap_16_t<u16>>;
671};
672
673template <>
674struct AddEndian<u32, SwapTag> {
675 using type = swap_struct_t<u32, swap_32_t<u32>>;
676};
632 677
633using u64_le = swap_struct_t<u64, swap_64_t<u64>>; 678template <>
634using s64_le = swap_struct_t<s64, swap_64_t<s64>>; 679struct AddEndian<u64, SwapTag> {
680 using type = swap_struct_t<u64, swap_64_t<u64>>;
681};
682
683template <>
684struct AddEndian<s8, SwapTag> {
685 using type = s8;
686};
635 687
636using u32_le = swap_struct_t<u32, swap_32_t<u32>>; 688template <>
637using s32_le = swap_struct_t<s32, swap_32_t<s32>>; 689struct AddEndian<s16, SwapTag> {
690 using type = swap_struct_t<s16, swap_16_t<s16>>;
691};
638 692
639using u16_le = swap_struct_t<u16, swap_16_t<u16>>; 693template <>
640using s16_le = swap_struct_t<s16, swap_16_t<s16>>; 694struct AddEndian<s32, SwapTag> {
695 using type = swap_struct_t<s32, swap_32_t<s32>>;
696};
697
698template <>
699struct AddEndian<s64, SwapTag> {
700 using type = swap_struct_t<s64, swap_64_t<s64>>;
701};
702
703template <>
704struct AddEndian<float, SwapTag> {
705 using type = swap_struct_t<float, swap_float_t<float>>;
706};
707
708template <>
709struct AddEndian<double, SwapTag> {
710 using type = swap_struct_t<double, swap_double_t<double>>;
711};
712
713template <typename T>
714struct AddEndian<T, SwapTag> {
715 static_assert(std::is_enum_v<T>);
716 using type = swap_enum_t<T>;
717};
641 718
642using float_le = swap_struct_t<float, swap_float_t<float>>; 719// Alias LETag/BETag as KeepTag/SwapTag depending on the system
643using double_le = swap_struct_t<double, swap_double_t<double>>; 720#if COMMON_LITTLE_ENDIAN
644 721
645using u16_be = u16; 722using LETag = KeepTag;
646using u32_be = u32; 723using BETag = SwapTag;
647using u64_be = u64;
648 724
649using s16_be = s16; 725#else
650using s32_be = s32;
651using s64_be = s64;
652 726
653using float_be = float; 727using BETag = KeepTag;
654using double_be = double; 728using LETag = SwapTag;
655 729
656#endif 730#endif
731
732// Aliases for LE types
733using u16_le = AddEndian<u16, LETag>::type;
734using u32_le = AddEndian<u32, LETag>::type;
735using u64_le = AddEndian<u64, LETag>::type;
736
737using s16_le = AddEndian<s16, LETag>::type;
738using s32_le = AddEndian<s32, LETag>::type;
739using s64_le = AddEndian<s64, LETag>::type;
740
741template <typename T>
742using enum_le = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, LETag>::type>;
743
744using float_le = AddEndian<float, LETag>::type;
745using double_le = AddEndian<double, LETag>::type;
746
747// Aliases for BE types
748using u16_be = AddEndian<u16, BETag>::type;
749using u32_be = AddEndian<u32, BETag>::type;
750using u64_be = AddEndian<u64, BETag>::type;
751
752using s16_be = AddEndian<s16, BETag>::type;
753using s32_be = AddEndian<s32, BETag>::type;
754using s64_be = AddEndian<s64, BETag>::type;
755
756template <typename T>
757using enum_be = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, BETag>::type>;
758
759using float_be = AddEndian<float, BETag>::type;
760using double_be = AddEndian<double, BETag>::type;
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 5144c0d9f..fe7a420cc 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -27,18 +27,6 @@ namespace Common {
27 27
28#ifdef _MSC_VER 28#ifdef _MSC_VER
29 29
30void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
31 SetThreadAffinityMask(thread, mask);
32}
33
34void SetCurrentThreadAffinity(u32 mask) {
35 SetThreadAffinityMask(GetCurrentThread(), mask);
36}
37
38void SwitchCurrentThread() {
39 SwitchToThread();
40}
41
42// Sets the debugger-visible name of the current thread. 30// Sets the debugger-visible name of the current thread.
43// Uses undocumented (actually, it is now documented) trick. 31// Uses undocumented (actually, it is now documented) trick.
44// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp 32// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
@@ -70,31 +58,6 @@ void SetCurrentThreadName(const char* name) {
70 58
71#else // !MSVC_VER, so must be POSIX threads 59#else // !MSVC_VER, so must be POSIX threads
72 60
73void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
74#ifdef __APPLE__
75 thread_policy_set(pthread_mach_thread_np(thread), THREAD_AFFINITY_POLICY, (integer_t*)&mask, 1);
76#elif (defined __linux__ || defined __FreeBSD__) && !(defined ANDROID)
77 cpu_set_t cpu_set;
78 CPU_ZERO(&cpu_set);
79
80 for (int i = 0; i != sizeof(mask) * 8; ++i)
81 if ((mask >> i) & 1)
82 CPU_SET(i, &cpu_set);
83
84 pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set);
85#endif
86}
87
88void SetCurrentThreadAffinity(u32 mask) {
89 SetThreadAffinity(pthread_self(), mask);
90}
91
92#ifndef _WIN32
93void SwitchCurrentThread() {
94 usleep(1000 * 1);
95}
96#endif
97
98// MinGW with the POSIX threading model does not support pthread_setname_np 61// MinGW with the POSIX threading model does not support pthread_setname_np
99#if !defined(_WIN32) || defined(_MSC_VER) 62#if !defined(_WIN32) || defined(_MSC_VER)
100void SetCurrentThreadName(const char* name) { 63void SetCurrentThreadName(const char* name) {
diff --git a/src/common/thread.h b/src/common/thread.h
index 2cf74452d..0cfd98be6 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -9,14 +9,13 @@
9#include <cstddef> 9#include <cstddef>
10#include <mutex> 10#include <mutex>
11#include <thread> 11#include <thread>
12#include "common/common_types.h"
13 12
14namespace Common { 13namespace Common {
15 14
16class Event { 15class Event {
17public: 16public:
18 void Set() { 17 void Set() {
19 std::lock_guard<std::mutex> lk(mutex); 18 std::lock_guard lk{mutex};
20 if (!is_set) { 19 if (!is_set) {
21 is_set = true; 20 is_set = true;
22 condvar.notify_one(); 21 condvar.notify_one();
@@ -24,14 +23,14 @@ public:
24 } 23 }
25 24
26 void Wait() { 25 void Wait() {
27 std::unique_lock<std::mutex> lk(mutex); 26 std::unique_lock lk{mutex};
28 condvar.wait(lk, [&] { return is_set; }); 27 condvar.wait(lk, [&] { return is_set; });
29 is_set = false; 28 is_set = false;
30 } 29 }
31 30
32 template <class Clock, class Duration> 31 template <class Clock, class Duration>
33 bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) { 32 bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
34 std::unique_lock<std::mutex> lk(mutex); 33 std::unique_lock lk{mutex};
35 if (!condvar.wait_until(lk, time, [this] { return is_set; })) 34 if (!condvar.wait_until(lk, time, [this] { return is_set; }))
36 return false; 35 return false;
37 is_set = false; 36 is_set = false;
@@ -39,7 +38,7 @@ public:
39 } 38 }
40 39
41 void Reset() { 40 void Reset() {
42 std::unique_lock<std::mutex> lk(mutex); 41 std::unique_lock lk{mutex};
43 // no other action required, since wait loops on the predicate and any lingering signal will 42 // no other action required, since wait loops on the predicate and any lingering signal will
44 // get cleared on the first iteration 43 // get cleared on the first iteration
45 is_set = false; 44 is_set = false;
@@ -57,7 +56,7 @@ public:
57 56
58 /// Blocks until all "count" threads have called Sync() 57 /// Blocks until all "count" threads have called Sync()
59 void Sync() { 58 void Sync() {
60 std::unique_lock<std::mutex> lk(mutex); 59 std::unique_lock lk{mutex};
61 const std::size_t current_generation = generation; 60 const std::size_t current_generation = generation;
62 61
63 if (++waiting == count) { 62 if (++waiting == count) {
@@ -78,9 +77,6 @@ private:
78 std::size_t generation = 0; // Incremented once each time the barrier is used 77 std::size_t generation = 0; // Incremented once each time the barrier is used
79}; 78};
80 79
81void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
82void SetCurrentThreadAffinity(u32 mask);
83void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
84void SetCurrentThreadName(const char* name); 80void SetCurrentThreadName(const char* name);
85 81
86} // namespace Common 82} // namespace Common
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index e7594db68..791f99a8c 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -6,7 +6,6 @@
6 6
7#include <array> 7#include <array>
8#include <deque> 8#include <deque>
9#include <boost/range/algorithm_ext/erase.hpp>
10 9
11namespace Common { 10namespace Common {
12 11
@@ -111,8 +110,9 @@ struct ThreadQueueList {
111 } 110 }
112 111
113 void remove(Priority priority, const T& thread_id) { 112 void remove(Priority priority, const T& thread_id) {
114 Queue* cur = &queues[priority]; 113 Queue* const cur = &queues[priority];
115 boost::remove_erase(cur->data, thread_id); 114 const auto iter = std::remove(cur->data.begin(), cur->data.end(), thread_id);
115 cur->data.erase(iter, cur->data.end());
116 } 116 }
117 117
118 void rotate(Priority priority) { 118 void rotate(Priority priority) {
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 821e8536a..e714ba5b3 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -78,7 +78,7 @@ public:
78 78
79 T PopWait() { 79 T PopWait() {
80 if (Empty()) { 80 if (Empty()) {
81 std::unique_lock<std::mutex> lock(cv_mutex); 81 std::unique_lock lock{cv_mutex};
82 cv.wait(lock, [this]() { return !Empty(); }); 82 cv.wait(lock, [this]() { return !Empty(); });
83 } 83 }
84 T t; 84 T t;
@@ -137,7 +137,7 @@ public:
137 137
138 template <typename Arg> 138 template <typename Arg>
139 void Push(Arg&& t) { 139 void Push(Arg&& t) {
140 std::lock_guard<std::mutex> lock(write_lock); 140 std::lock_guard lock{write_lock};
141 spsc_queue.Push(t); 141 spsc_queue.Push(t);
142 } 142 }
143 143
diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp
new file mode 100644
index 000000000..32bf56730
--- /dev/null
+++ b/src/common/uint128.cpp
@@ -0,0 +1,45 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#ifdef _MSC_VER
6#include <intrin.h>
7
8#pragma intrinsic(_umul128)
9#endif
10#include <cstring>
11#include "common/uint128.h"
12
13namespace Common {
14
15u128 Multiply64Into128(u64 a, u64 b) {
16 u128 result;
17#ifdef _MSC_VER
18 result[0] = _umul128(a, b, &result[1]);
19#else
20 unsigned __int128 tmp = a;
21 tmp *= b;
22 std::memcpy(&result, &tmp, sizeof(u128));
23#endif
24 return result;
25}
26
27std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
28 u64 remainder = dividend[0] % divisor;
29 u64 accum = dividend[0] / divisor;
30 if (dividend[1] == 0)
31 return {accum, remainder};
32 // We ignore dividend[1] / divisor as that overflows
33 const u64 first_segment = (dividend[1] % divisor) << 32;
34 accum += (first_segment / divisor) << 32;
35 const u64 second_segment = (first_segment % divisor) << 32;
36 accum += (second_segment / divisor);
37 remainder += second_segment % divisor;
38 if (remainder >= divisor) {
39 accum++;
40 remainder -= divisor;
41 }
42 return {accum, remainder};
43}
44
45} // namespace Common
diff --git a/src/common/uint128.h b/src/common/uint128.h
new file mode 100644
index 000000000..a3be2a2cb
--- /dev/null
+++ b/src/common/uint128.h
@@ -0,0 +1,19 @@
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 <utility>
8#include "common/common_types.h"
9
10namespace Common {
11
12// This function multiplies 2 u64 values and produces a u128 value;
13u128 Multiply64Into128(u64 a, u64 b);
14
15// This function divides a u128 by a u32 value and produces two u64 values:
16// the result of division and the remainder
17std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
18
19} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 8ccb2d5f0..c59107102 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -31,6 +31,8 @@ add_library(core STATIC
31 file_sys/bis_factory.h 31 file_sys/bis_factory.h
32 file_sys/card_image.cpp 32 file_sys/card_image.cpp
33 file_sys/card_image.h 33 file_sys/card_image.h
34 file_sys/cheat_engine.cpp
35 file_sys/cheat_engine.h
34 file_sys/content_archive.cpp 36 file_sys/content_archive.cpp
35 file_sys/content_archive.h 37 file_sys/content_archive.h
36 file_sys/control_metadata.cpp 38 file_sys/control_metadata.cpp
@@ -68,6 +70,8 @@ add_library(core STATIC
68 file_sys/system_archive/ng_word.h 70 file_sys/system_archive/ng_word.h
69 file_sys/system_archive/system_archive.cpp 71 file_sys/system_archive/system_archive.cpp
70 file_sys/system_archive/system_archive.h 72 file_sys/system_archive/system_archive.h
73 file_sys/system_archive/system_version.cpp
74 file_sys/system_archive/system_version.h
71 file_sys/vfs.cpp 75 file_sys/vfs.cpp
72 file_sys/vfs.h 76 file_sys/vfs.h
73 file_sys/vfs_concat.cpp 77 file_sys/vfs_concat.cpp
@@ -107,6 +111,8 @@ add_library(core STATIC
107 hle/kernel/client_port.h 111 hle/kernel/client_port.h
108 hle/kernel/client_session.cpp 112 hle/kernel/client_session.cpp
109 hle/kernel/client_session.h 113 hle/kernel/client_session.h
114 hle/kernel/code_set.cpp
115 hle/kernel/code_set.h
110 hle/kernel/errors.h 116 hle/kernel/errors.h
111 hle/kernel/handle_table.cpp 117 hle/kernel/handle_table.cpp
112 hle/kernel/handle_table.h 118 hle/kernel/handle_table.h
@@ -140,6 +146,8 @@ add_library(core STATIC
140 hle/kernel/svc_wrap.h 146 hle/kernel/svc_wrap.h
141 hle/kernel/thread.cpp 147 hle/kernel/thread.cpp
142 hle/kernel/thread.h 148 hle/kernel/thread.h
149 hle/kernel/transfer_memory.cpp
150 hle/kernel/transfer_memory.h
143 hle/kernel/vm_manager.cpp 151 hle/kernel/vm_manager.cpp
144 hle/kernel/vm_manager.h 152 hle/kernel/vm_manager.h
145 hle/kernel/wait_object.cpp 153 hle/kernel/wait_object.cpp
@@ -419,8 +427,6 @@ add_library(core STATIC
419 loader/deconstructed_rom_directory.h 427 loader/deconstructed_rom_directory.h
420 loader/elf.cpp 428 loader/elf.cpp
421 loader/elf.h 429 loader/elf.h
422 loader/linker.cpp
423 loader/linker.h
424 loader/loader.cpp 430 loader/loader.cpp
425 loader/loader.h 431 loader/loader.h
426 loader/nax.cpp 432 loader/nax.cpp
@@ -437,8 +443,6 @@ add_library(core STATIC
437 loader/xci.h 443 loader/xci.h
438 memory.cpp 444 memory.cpp
439 memory.h 445 memory.h
440 memory_hook.cpp
441 memory_hook.h
442 memory_setup.h 446 memory_setup.h
443 perf_stats.cpp 447 perf_stats.cpp
444 perf_stats.h 448 perf_stats.h
@@ -454,7 +458,7 @@ add_library(core STATIC
454create_target_directory_groups(core) 458create_target_directory_groups(core)
455 459
456target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 460target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
457target_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)
458if (ENABLE_WEB_SERVICE) 462if (ENABLE_WEB_SERVICE)
459 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) 463 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
460 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 9b7ca4030..f64e4c6a6 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -12,6 +12,7 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/core_cpu.h" 13#include "core/core_cpu.h"
14#include "core/core_timing.h" 14#include "core/core_timing.h"
15#include "core/core_timing_util.h"
15#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
16#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/svc.h" 18#include "core/hle/kernel/svc.h"
@@ -25,7 +26,6 @@ using Vector = Dynarmic::A64::Vector;
25class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks { 26class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
26public: 27public:
27 explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} 28 explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
28 ~ARM_Dynarmic_Callbacks() = default;
29 29
30 u8 MemoryRead8(u64 vaddr) override { 30 u8 MemoryRead8(u64 vaddr) override {
31 return Memory::Read8(vaddr); 31 return Memory::Read8(vaddr);
@@ -119,7 +119,7 @@ public:
119 return std::max(parent.core_timing.GetDowncount(), 0); 119 return std::max(parent.core_timing.GetDowncount(), 0);
120 } 120 }
121 u64 GetCNTPCT() override { 121 u64 GetCNTPCT() override {
122 return parent.core_timing.GetTicks(); 122 return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks());
123 } 123 }
124 124
125 ARM_Dynarmic& parent; 125 ARM_Dynarmic& parent;
@@ -151,7 +151,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
151 config.tpidr_el0 = &cb->tpidr_el0; 151 config.tpidr_el0 = &cb->tpidr_el0;
152 config.dczid_el0 = 4; 152 config.dczid_el0 = 4;
153 config.ctr_el0 = 0x8444c004; 153 config.ctr_el0 = 0x8444c004;
154 config.cntfrq_el0 = 19200000; // Value from fusee. 154 config.cntfrq_el0 = Timing::CNTFREQ;
155 155
156 // Unpredictable instructions 156 // Unpredictable instructions
157 config.define_unpredictable_behaviour = true; 157 config.define_unpredictable_behaviour = true;
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 6cc458296..81e0b4ac0 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -12,7 +12,7 @@
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 Memory { 15namespace Common {
16struct PageTable; 16struct PageTable;
17} 17}
18 18
@@ -29,7 +29,7 @@ class ARM_Dynarmic final : public ARM_Interface {
29public: 29public:
30 ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, 30 ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
31 std::size_t core_index); 31 std::size_t core_index);
32 ~ARM_Dynarmic(); 32 ~ARM_Dynarmic() override;
33 33
34 void MapBackingMemory(VAddr address, std::size_t size, u8* memory, 34 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
35 Kernel::VMAPermission perms) override; 35 Kernel::VMAPermission perms) override;
@@ -70,13 +70,13 @@ private:
70 Timing::CoreTiming& core_timing; 70 Timing::CoreTiming& core_timing;
71 DynarmicExclusiveMonitor& exclusive_monitor; 71 DynarmicExclusiveMonitor& exclusive_monitor;
72 72
73 Memory::PageTable* current_page_table = nullptr; 73 Common::PageTable* current_page_table = nullptr;
74}; 74};
75 75
76class DynarmicExclusiveMonitor final : public ExclusiveMonitor { 76class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
77public: 77public:
78 explicit DynarmicExclusiveMonitor(std::size_t core_count); 78 explicit DynarmicExclusiveMonitor(std::size_t core_count);
79 ~DynarmicExclusiveMonitor(); 79 ~DynarmicExclusiveMonitor() override;
80 80
81 void SetExclusive(std::size_t core_index, VAddr addr) override; 81 void SetExclusive(std::size_t core_index, VAddr addr) override;
82 void ClearExclusive() override; 82 void ClearExclusive() override;
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index 893e11388..1e44f0736 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -18,7 +18,7 @@ namespace Core {
18class ARM_Unicorn final : public ARM_Interface { 18class ARM_Unicorn final : public ARM_Interface {
19public: 19public:
20 explicit ARM_Unicorn(Timing::CoreTiming& core_timing); 20 explicit ARM_Unicorn(Timing::CoreTiming& core_timing);
21 ~ARM_Unicorn(); 21 ~ARM_Unicorn() override;
22 22
23 void MapBackingMemory(VAddr address, std::size_t size, u8* memory, 23 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
24 Kernel::VMAPermission perms) override; 24 Kernel::VMAPermission perms) override;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 89b3fb418..4fe77c25b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -32,6 +32,7 @@
32#include "core/perf_stats.h" 32#include "core/perf_stats.h"
33#include "core/settings.h" 33#include "core/settings.h"
34#include "core/telemetry_session.h" 34#include "core/telemetry_session.h"
35#include "file_sys/cheat_engine.h"
35#include "frontend/applets/profile_select.h" 36#include "frontend/applets/profile_select.h"
36#include "frontend/applets/software_keyboard.h" 37#include "frontend/applets/software_keyboard.h"
37#include "frontend/applets/web_browser.h" 38#include "frontend/applets/web_browser.h"
@@ -205,6 +206,7 @@ struct System::Impl {
205 GDBStub::Shutdown(); 206 GDBStub::Shutdown();
206 Service::Shutdown(); 207 Service::Shutdown();
207 service_manager.reset(); 208 service_manager.reset();
209 cheat_engine.reset();
208 telemetry_session.reset(); 210 telemetry_session.reset();
209 gpu_core.reset(); 211 gpu_core.reset();
210 212
@@ -255,6 +257,8 @@ struct System::Impl {
255 CpuCoreManager cpu_core_manager; 257 CpuCoreManager cpu_core_manager;
256 bool is_powered_on = false; 258 bool is_powered_on = false;
257 259
260 std::unique_ptr<FileSys::CheatEngine> cheat_engine;
261
258 /// Frontend applets 262 /// Frontend applets
259 std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector; 263 std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
260 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; 264 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
@@ -453,6 +457,13 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
453 return impl->debug_context.get(); 457 return impl->debug_context.get();
454} 458}
455 459
460void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
461 const std::string& build_id, VAddr code_region_start,
462 VAddr code_region_end) {
463 impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id,
464 code_region_start, code_region_end);
465}
466
456void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { 467void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
457 impl->virtual_filesystem = std::move(vfs); 468 impl->virtual_filesystem = std::move(vfs);
458} 469}
diff --git a/src/core/core.h b/src/core/core.h
index ba76a41d8..4d83b93cc 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -20,6 +20,7 @@ class WebBrowserApplet;
20} // namespace Core::Frontend 20} // namespace Core::Frontend
21 21
22namespace FileSys { 22namespace FileSys {
23class CheatList;
23class VfsFilesystem; 24class VfsFilesystem;
24} // namespace FileSys 25} // namespace FileSys
25 26
@@ -253,6 +254,9 @@ public:
253 254
254 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; 255 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
255 256
257 void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
258 VAddr code_region_start, VAddr code_region_end);
259
256 void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet); 260 void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
257 261
258 const Frontend::ProfileSelectApplet& GetProfileSelector() const; 262 const Frontend::ProfileSelectApplet& GetProfileSelector() const;
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 1eefed6d0..e75741db0 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -22,7 +22,7 @@
22namespace Core { 22namespace Core {
23 23
24void CpuBarrier::NotifyEnd() { 24void CpuBarrier::NotifyEnd() {
25 std::unique_lock<std::mutex> lock(mutex); 25 std::unique_lock lock{mutex};
26 end = true; 26 end = true;
27 condition.notify_all(); 27 condition.notify_all();
28} 28}
@@ -34,7 +34,7 @@ bool CpuBarrier::Rendezvous() {
34 } 34 }
35 35
36 if (!end) { 36 if (!end) {
37 std::unique_lock<std::mutex> lock(mutex); 37 std::unique_lock lock{mutex};
38 38
39 --cores_waiting; 39 --cores_waiting;
40 if (!cores_waiting) { 40 if (!cores_waiting) {
@@ -131,7 +131,7 @@ void Cpu::Reschedule() {
131 131
132 reschedule_pending = false; 132 reschedule_pending = false;
133 // Lock the global kernel mutex when we manipulate the HLE state 133 // Lock the global kernel mutex when we manipulate the HLE state
134 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 134 std::lock_guard lock{HLE::g_hle_lock};
135 scheduler->Reschedule(); 135 scheduler->Reschedule();
136} 136}
137 137
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index a0dd5db24..41adb2302 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -186,7 +186,7 @@ void CoreTiming::Advance() {
186 Event evt = std::move(event_queue.front()); 186 Event evt = std::move(event_queue.front());
187 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 187 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
188 event_queue.pop_back(); 188 event_queue.pop_back();
189 evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time)); 189 evt.type->callback(evt.userdata, global_timer - evt.time);
190 } 190 }
191 191
192 is_global_timer_sane = false; 192 is_global_timer_sane = false;
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 59163bae1..9d2efde37 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -15,7 +15,7 @@
15namespace Core::Timing { 15namespace Core::Timing {
16 16
17/// A callback that may be scheduled for a particular core timing event. 17/// A callback that may be scheduled for a particular core timing event.
18using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; 18using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>;
19 19
20/// Contains the characteristics of a particular event. 20/// Contains the characteristics of a particular event.
21struct EventType { 21struct EventType {
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index 88ff70233..7942f30d6 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -7,6 +7,7 @@
7#include <cinttypes> 7#include <cinttypes>
8#include <limits> 8#include <limits>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/uint128.h"
10 11
11namespace Core::Timing { 12namespace Core::Timing {
12 13
@@ -60,4 +61,9 @@ s64 nsToCycles(u64 ns) {
60 return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000; 61 return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
61} 62}
62 63
64u64 CpuCyclesToClockCycles(u64 ticks) {
65 const u128 temporal = Common::Multiply64Into128(ticks, CNTFREQ);
66 return Common::Divide128On32(temporal, static_cast<u32>(BASE_CLOCK_RATE)).first;
67}
68
63} // namespace Core::Timing 69} // namespace Core::Timing
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 513cfac1b..679aa3123 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -11,6 +11,7 @@ namespace Core::Timing {
11// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz 11// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
12// The exact value used is of course unverified. 12// The exact value used is of course unverified.
13constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked 13constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
14constexpr u64 CNTFREQ = 19200000; // Value from fusee.
14 15
15inline s64 msToCycles(int ms) { 16inline s64 msToCycles(int ms) {
16 // since ms is int there is no way to overflow 17 // since ms is int there is no way to overflow
@@ -61,4 +62,6 @@ inline u64 cyclesToMs(s64 cycles) {
61 return cycles * 1000 / BASE_CLOCK_RATE; 62 return cycles * 1000 / BASE_CLOCK_RATE;
62} 63}
63 64
65u64 CpuCyclesToClockCycles(u64 ticks);
66
64} // namespace Core::Timing 67} // namespace Core::Timing
diff --git a/src/core/file_sys/cheat_engine.cpp b/src/core/file_sys/cheat_engine.cpp
new file mode 100644
index 000000000..b06c2f20a
--- /dev/null
+++ b/src/core/file_sys/cheat_engine.cpp
@@ -0,0 +1,492 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <locale>
6#include "common/hex_util.h"
7#include "common/microprofile.h"
8#include "common/swap.h"
9#include "core/core.h"
10#include "core/core_timing.h"
11#include "core/core_timing_util.h"
12#include "core/file_sys/cheat_engine.h"
13#include "core/hle/kernel/process.h"
14#include "core/hle/service/hid/controllers/npad.h"
15#include "core/hle/service/hid/hid.h"
16#include "core/hle/service/sm/sm.h"
17
18namespace FileSys {
19
20constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
21constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
22
23u64 Cheat::Address() const {
24 u64 out;
25 std::memcpy(&out, raw.data(), sizeof(u64));
26 return Common::swap64(out) & 0xFFFFFFFFFF;
27}
28
29u64 Cheat::ValueWidth(u64 offset) const {
30 return Value(offset, width);
31}
32
33u64 Cheat::Value(u64 offset, u64 width) const {
34 u64 out;
35 std::memcpy(&out, raw.data() + offset, sizeof(u64));
36 out = Common::swap64(out);
37 if (width == 8)
38 return out;
39 return out & ((1ull << (width * CHAR_BIT)) - 1);
40}
41
42u32 Cheat::KeypadValue() const {
43 u32 out;
44 std::memcpy(&out, raw.data(), sizeof(u32));
45 return Common::swap32(out) & 0x0FFFFFFF;
46}
47
48void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
49 VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
50 this->main_region_begin = main_begin;
51 this->main_region_end = main_end;
52 this->heap_region_begin = heap_begin;
53 this->heap_region_end = heap_end;
54 this->writer = writer;
55 this->reader = reader;
56}
57
58MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
59
60void CheatList::Execute() {
61 MICROPROFILE_SCOPE(Cheat_Engine);
62
63 std::fill(scratch.begin(), scratch.end(), 0);
64 in_standard = false;
65 for (std::size_t i = 0; i < master_list.size(); ++i) {
66 LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
67 current_block = i;
68 ExecuteBlock(master_list[i].second);
69 }
70
71 in_standard = true;
72 for (std::size_t i = 0; i < standard_list.size(); ++i) {
73 LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
74 current_block = i;
75 ExecuteBlock(standard_list[i].second);
76 }
77}
78
79CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard)
80 : master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {}
81
82bool CheatList::EvaluateConditional(const Cheat& cheat) const {
83 using ComparisonFunction = bool (*)(u64, u64);
84 constexpr std::array<ComparisonFunction, 6> comparison_functions{
85 [](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; },
86 [](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; },
87 [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
88 };
89
90 if (cheat.type == CodeType::ConditionalInput) {
91 const auto applet_resource =
92 system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
93 if (applet_resource == nullptr) {
94 LOG_WARNING(
95 Common_Filesystem,
96 "Attempted to evaluate input conditional, but applet resource is not initialized!");
97 return false;
98 }
99
100 const auto press_state =
101 applet_resource
102 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
103 .GetAndResetPressState();
104 return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
105 }
106
107 ASSERT(cheat.type == CodeType::Conditional);
108
109 const auto offset =
110 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
111 ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
112 auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
113 const auto addr = cheat.Address() + offset;
114
115 return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
116}
117
118void CheatList::ProcessBlockPairs(const Block& block) {
119 block_pairs.clear();
120
121 u64 scope = 0;
122 std::map<u64, u64> pairs;
123
124 for (std::size_t i = 0; i < block.size(); ++i) {
125 const auto& cheat = block[i];
126
127 switch (cheat.type) {
128 case CodeType::Conditional:
129 case CodeType::ConditionalInput:
130 pairs.insert_or_assign(scope, i);
131 ++scope;
132 break;
133 case CodeType::EndConditional: {
134 --scope;
135 const auto idx = pairs.at(scope);
136 block_pairs.insert_or_assign(idx, i);
137 break;
138 }
139 case CodeType::Loop: {
140 if (cheat.end_of_loop) {
141 --scope;
142 const auto idx = pairs.at(scope);
143 block_pairs.insert_or_assign(idx, i);
144 } else {
145 pairs.insert_or_assign(scope, i);
146 ++scope;
147 }
148 break;
149 }
150 }
151 }
152}
153
154void CheatList::WriteImmediate(const Cheat& cheat) {
155 const auto offset =
156 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
157 const auto& register_3 = scratch.at(cheat.register_3);
158
159 const auto addr = cheat.Address() + offset + register_3;
160 LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
161 cheat.Value(8, cheat.width));
162 writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
163}
164
165void CheatList::BeginConditional(const Cheat& cheat) {
166 if (EvaluateConditional(cheat)) {
167 return;
168 }
169
170 const auto iter = block_pairs.find(current_index);
171 ASSERT(iter != block_pairs.end());
172 current_index = iter->second - 1;
173}
174
175void CheatList::EndConditional(const Cheat& cheat) {
176 LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
177}
178
179void CheatList::Loop(const Cheat& cheat) {
180 if (cheat.end_of_loop.Value())
181 ASSERT(!cheat.end_of_loop.Value());
182
183 auto& register_3 = scratch.at(cheat.register_3);
184 const auto iter = block_pairs.find(current_index);
185 ASSERT(iter != block_pairs.end());
186 ASSERT(iter->first < iter->second);
187
188 const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32)));
189 for (s32 i = initial_value; i >= 0; --i) {
190 register_3 = static_cast<u64>(i);
191 for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
192 current_index = c;
193 ExecuteSingleCheat(
194 (in_standard ? standard_list : master_list)[current_block].second[c]);
195 }
196 }
197
198 current_index = iter->second;
199}
200
201void CheatList::LoadImmediate(const Cheat& cheat) {
202 auto& register_3 = scratch.at(cheat.register_3);
203
204 LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
205 cheat.Value(4, 8));
206 register_3 = cheat.Value(4, 8);
207}
208
209void CheatList::LoadIndexed(const Cheat& cheat) {
210 const auto offset =
211 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
212 auto& register_3 = scratch.at(cheat.register_3);
213
214 const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
215 LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
216 cheat.register_3, addr);
217 register_3 = reader(cheat.width, SanitizeAddress(addr));
218}
219
220void CheatList::StoreIndexed(const Cheat& cheat) {
221 const auto& register_3 = scratch.at(cheat.register_3);
222
223 const auto addr =
224 register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
225 LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
226 cheat.Value(4, cheat.width), addr);
227 writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
228}
229
230void CheatList::RegisterArithmetic(const Cheat& cheat) {
231 using ArithmeticFunction = u64 (*)(u64, u64);
232 constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
233 [](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
234 [](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
235 [](u64 a, u64 b) { return a >> b; },
236 };
237
238 using ArithmeticOverflowCheck = bool (*)(u64, u64);
239 constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
240 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
241 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
242 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
243 [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
244 [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
245 };
246
247 static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
248 "Missing or have extra arithmetic overflow checks compared to functions!");
249
250 auto& register_3 = scratch.at(cheat.register_3);
251
252 ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
253 auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
254 auto* overflow_function =
255 arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
256 LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
257 cheat.register_3, cheat.ValueWidth(4));
258
259 if (overflow_function(register_3, cheat.ValueWidth(4))) {
260 LOG_WARNING(Common_Filesystem,
261 "overflow will occur when performing arithmetic operation={:02X} with operands "
262 "a={:016X}, b={:016X}!",
263 static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
264 }
265
266 register_3 = function(register_3, cheat.ValueWidth(4));
267}
268
269void CheatList::BeginConditionalInput(const Cheat& cheat) {
270 if (EvaluateConditional(cheat))
271 return;
272
273 const auto iter = block_pairs.find(current_index);
274 ASSERT(iter != block_pairs.end());
275 current_index = iter->second - 1;
276}
277
278VAddr CheatList::SanitizeAddress(VAddr in) const {
279 if ((in < main_region_begin || in >= main_region_end) &&
280 (in < heap_region_begin || in >= heap_region_end)) {
281 LOG_ERROR(Common_Filesystem,
282 "Cheat attempting to access memory at invalid address={:016X}, if this persists, "
283 "the cheat may be incorrect. However, this may be normal early in execution if "
284 "the game has not properly set up yet.",
285 in);
286 return 0; ///< Invalid addresses will hard crash
287 }
288
289 return in;
290}
291
292void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
293 using CheatOperationFunction = void (CheatList::*)(const Cheat&);
294 constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
295 &CheatList::WriteImmediate, &CheatList::BeginConditional,
296 &CheatList::EndConditional, &CheatList::Loop,
297 &CheatList::LoadImmediate, &CheatList::LoadIndexed,
298 &CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
299 &CheatList::BeginConditionalInput,
300 };
301
302 const auto index = static_cast<u8>(cheat.type.Value());
303 ASSERT(index < sizeof(cheat_operation_functions));
304 const auto op = cheat_operation_functions[index];
305 (this->*op)(cheat);
306}
307
308void CheatList::ExecuteBlock(const Block& block) {
309 encountered_loops.clear();
310
311 ProcessBlockPairs(block);
312 for (std::size_t i = 0; i < block.size(); ++i) {
313 current_index = i;
314 ExecuteSingleCheat(block[i]);
315 i = current_index;
316 }
317}
318
319CheatParser::~CheatParser() = default;
320
321CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master,
322 CheatList::ProgramSegment standard) const {
323 return {system, std::move(master), std::move(standard)};
324}
325
326TextCheatParser::~TextCheatParser() = default;
327
328CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const {
329 std::stringstream ss;
330 ss.write(reinterpret_cast<const char*>(data.data()), data.size());
331
332 std::vector<std::string> lines;
333 std::string stream_line;
334 while (std::getline(ss, stream_line)) {
335 // Remove a trailing \r
336 if (!stream_line.empty() && stream_line.back() == '\r')
337 stream_line.pop_back();
338 lines.push_back(std::move(stream_line));
339 }
340
341 CheatList::ProgramSegment master_list;
342 CheatList::ProgramSegment standard_list;
343
344 for (std::size_t i = 0; i < lines.size(); ++i) {
345 auto line = lines[i];
346
347 if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
348 const auto master = line[0] == '{';
349 const auto begin = master ? line.find('{') : line.find('[');
350 const auto end = master ? line.rfind('}') : line.rfind(']');
351
352 ASSERT(begin != std::string::npos && end != std::string::npos);
353
354 const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
355 CheatList::Block block{};
356
357 while (i < lines.size() - 1) {
358 line = lines[++i];
359 if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
360 --i;
361 break;
362 }
363
364 if (line.size() < 8)
365 continue;
366
367 Cheat out{};
368 out.raw = ParseSingleLineCheat(line);
369 block.push_back(out);
370 }
371
372 (master ? master_list : standard_list).emplace_back(patch_name, block);
373 }
374 }
375
376 return MakeCheatList(system, master_list, standard_list);
377}
378
379std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
380 std::array<u8, 16> out{};
381
382 if (line.size() < 8)
383 return out;
384
385 const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
386 std::memcpy(out.data(), word1.data(), sizeof(u32));
387
388 if (line.size() < 17 || line[8] != ' ')
389 return out;
390
391 const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
392 std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
393
394 if (line.size() < 26 || line[17] != ' ') {
395 // Perform shifting in case value is truncated early.
396 const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
397 if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
398 type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
399 std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
400 std::memset(out.data() + 4, 0, sizeof(u32));
401 }
402
403 return out;
404 }
405
406 const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
407 std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
408
409 if (line.size() < 35 || line[26] != ' ') {
410 // Perform shifting in case value is truncated early.
411 const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
412 if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
413 std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
414 std::memset(out.data() + 8, 0, sizeof(u32));
415 }
416
417 return out;
418 }
419
420 const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
421 std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
422
423 return out;
424}
425
426namespace {
427u64 MemoryReadImpl(u32 width, VAddr addr) {
428 switch (width) {
429 case 1:
430 return Memory::Read8(addr);
431 case 2:
432 return Memory::Read16(addr);
433 case 4:
434 return Memory::Read32(addr);
435 case 8:
436 return Memory::Read64(addr);
437 default:
438 UNREACHABLE();
439 return 0;
440 }
441}
442
443void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
444 switch (width) {
445 case 1:
446 Memory::Write8(addr, static_cast<u8>(value));
447 break;
448 case 2:
449 Memory::Write16(addr, static_cast<u16>(value));
450 break;
451 case 4:
452 Memory::Write32(addr, static_cast<u32>(value));
453 break;
454 case 8:
455 Memory::Write64(addr, value);
456 break;
457 default:
458 UNREACHABLE();
459 }
460}
461} // Anonymous namespace
462
463CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_,
464 const std::string& build_id, VAddr code_region_start,
465 VAddr code_region_end)
466 : cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} {
467 event = core_timing.RegisterEvent(
468 "CheatEngine::FrameCallback::" + build_id,
469 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
470 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
471
472 const auto& vm_manager = system.CurrentProcess()->VMManager();
473 for (auto& list : this->cheats) {
474 list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
475 code_region_end, vm_manager.GetHeapRegionEndAddress(),
476 &MemoryWriteImpl, &MemoryReadImpl);
477 }
478}
479
480CheatEngine::~CheatEngine() {
481 core_timing.UnscheduleEvent(event, 0);
482}
483
484void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
485 for (auto& list : cheats) {
486 list.Execute();
487 }
488
489 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
490}
491
492} // namespace FileSys
diff --git a/src/core/file_sys/cheat_engine.h b/src/core/file_sys/cheat_engine.h
new file mode 100644
index 000000000..ac22a82cb
--- /dev/null
+++ b/src/core/file_sys/cheat_engine.h
@@ -0,0 +1,234 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <set>
9#include <vector>
10#include "common/bit_field.h"
11#include "common/common_types.h"
12
13namespace Core {
14class System;
15}
16
17namespace Core::Timing {
18class CoreTiming;
19struct EventType;
20} // namespace Core::Timing
21
22namespace FileSys {
23
24enum class CodeType : u32 {
25 // 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
26 // Writes a T sized value Y to the address A added to the value of register R in memory domain M
27 WriteImmediate = 0,
28
29 // 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
30 // Compares the T sized value Y to the value at address A in memory domain M using the
31 // conditional function C. If success, continues execution. If failure, jumps to the matching
32 // EndConditional statement.
33 Conditional = 1,
34
35 // 20000000
36 // Terminates a Conditional or ConditionalInput block.
37 EndConditional = 2,
38
39 // 300R0000 VVVVVVVV
40 // Starts looping V times, storing the current count in register R.
41 // Loop block is terminated with a matching 310R0000.
42 Loop = 3,
43
44 // 400R0000 VVVVVVVV VVVVVVVV
45 // Sets the value of register R to the value V.
46 LoadImmediate = 4,
47
48 // 5TMRI0AA AAAAAAAA
49 // Sets the value of register R to the value of width T at address A in memory domain M, with
50 // the current value of R added to the address if I == 1.
51 LoadIndexed = 5,
52
53 // 6T0RIFG0 VVVVVVVV VVVVVVVV
54 // Writes the value V of width T to the memory address stored in register R. Adds the value of
55 // register G to the final calculation if F is nonzero. Increments the value of register R by T
56 // after operation if I is nonzero.
57 StoreIndexed = 6,
58
59 // 7T0RA000 VVVVVVVV
60 // Performs the arithmetic operation A on the value in register R and the value V of width T,
61 // storing the result in register R.
62 RegisterArithmetic = 7,
63
64 // 8KKKKKKK
65 // Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
66 // execution continues. If none are, execution skips to the next EndConditional command.
67 ConditionalInput = 8,
68};
69
70enum class MemoryType : u32 {
71 // Addressed relative to start of main NSO
72 MainNSO = 0,
73
74 // Addressed relative to start of heap
75 Heap = 1,
76};
77
78enum class ArithmeticOp : u32 {
79 Add = 0,
80 Sub = 1,
81 Mult = 2,
82 LShift = 3,
83 RShift = 4,
84};
85
86enum class ComparisonOp : u32 {
87 GreaterThan = 1,
88 GreaterThanEqual = 2,
89 LessThan = 3,
90 LessThanEqual = 4,
91 Equal = 5,
92 Inequal = 6,
93};
94
95union Cheat {
96 std::array<u8, 16> raw;
97
98 BitField<4, 4, CodeType> type;
99 BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
100 BitField<0, 4, u32> end_of_loop;
101 BitField<12, 4, MemoryType> memory_type;
102 BitField<8, 4, u32> register_3;
103 BitField<8, 4, ComparisonOp> comparison_op;
104 BitField<20, 4, u32> load_from_register;
105 BitField<20, 4, u32> increment_register;
106 BitField<20, 4, ArithmeticOp> arithmetic_op;
107 BitField<16, 4, u32> add_additional_register;
108 BitField<28, 4, u32> register_6;
109
110 u64 Address() const;
111 u64 ValueWidth(u64 offset) const;
112 u64 Value(u64 offset, u64 width) const;
113 u32 KeypadValue() const;
114};
115
116class CheatParser;
117
118// Represents a full collection of cheats for a game. The Execute function should be called every
119// interval that all cheats should be executed. Clients should not directly instantiate this class
120// (hence private constructor), they should instead receive an instance from CheatParser, which
121// guarantees the list is always in an acceptable state.
122class CheatList {
123public:
124 friend class CheatParser;
125
126 using Block = std::vector<Cheat>;
127 using ProgramSegment = std::vector<std::pair<std::string, Block>>;
128
129 // (width in bytes, address, value)
130 using MemoryWriter = void (*)(u32, VAddr, u64);
131 // (width in bytes, address) -> value
132 using MemoryReader = u64 (*)(u32, VAddr);
133
134 void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
135 MemoryWriter writer, MemoryReader reader);
136
137 void Execute();
138
139private:
140 CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard);
141
142 void ProcessBlockPairs(const Block& block);
143 void ExecuteSingleCheat(const Cheat& cheat);
144
145 void ExecuteBlock(const Block& block);
146
147 bool EvaluateConditional(const Cheat& cheat) const;
148
149 // Individual cheat operations
150 void WriteImmediate(const Cheat& cheat);
151 void BeginConditional(const Cheat& cheat);
152 void EndConditional(const Cheat& cheat);
153 void Loop(const Cheat& cheat);
154 void LoadImmediate(const Cheat& cheat);
155 void LoadIndexed(const Cheat& cheat);
156 void StoreIndexed(const Cheat& cheat);
157 void RegisterArithmetic(const Cheat& cheat);
158 void BeginConditionalInput(const Cheat& cheat);
159
160 VAddr SanitizeAddress(VAddr in) const;
161
162 // Master Codes are defined as codes that cannot be disabled and are run prior to all
163 // others.
164 ProgramSegment master_list;
165 // All other codes
166 ProgramSegment standard_list;
167
168 bool in_standard = false;
169
170 // 16 (0x0-0xF) scratch registers that can be used by cheats
171 std::array<u64, 16> scratch{};
172
173 MemoryWriter writer = nullptr;
174 MemoryReader reader = nullptr;
175
176 u64 main_region_begin{};
177 u64 heap_region_begin{};
178 u64 main_region_end{};
179 u64 heap_region_end{};
180
181 u64 current_block{};
182 // The current index of the cheat within the current Block
183 u64 current_index{};
184
185 // The 'stack' of the program. When a conditional or loop statement is encountered, its index is
186 // pushed onto this queue. When a end block is encountered, the condition is checked.
187 std::map<u64, u64> block_pairs;
188
189 std::set<u64> encountered_loops;
190
191 const Core::System* system;
192};
193
194// Intermediary class that parses a text file or other disk format for storing cheats into a
195// CheatList object, that can be used for execution.
196class CheatParser {
197public:
198 virtual ~CheatParser();
199
200 virtual CheatList Parse(const Core::System& system, const std::vector<u8>& data) const = 0;
201
202protected:
203 CheatList MakeCheatList(const Core::System& system_, CheatList::ProgramSegment master,
204 CheatList::ProgramSegment standard) const;
205};
206
207// CheatParser implementation that parses text files
208class TextCheatParser final : public CheatParser {
209public:
210 ~TextCheatParser() override;
211
212 CheatList Parse(const Core::System& system, const std::vector<u8>& data) const override;
213
214private:
215 std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
216};
217
218// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
219class CheatEngine final {
220public:
221 CheatEngine(Core::System& system_, std::vector<CheatList> cheats_, const std::string& build_id,
222 VAddr code_region_start, VAddr code_region_end);
223 ~CheatEngine();
224
225private:
226 void FrameCallback(u64 userdata, s64 cycles_late);
227
228 std::vector<CheatList> cheats;
229
230 Core::Timing::EventType* event;
231 Core::Timing::CoreTiming& core_timing;
232};
233
234} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 5d4d05c82..15b9e6624 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -24,13 +24,26 @@ namespace FileSys {
24 24
25union NCASectionHeader; 25union NCASectionHeader;
26 26
27/// Describes the type of content within an NCA archive.
27enum class NCAContentType : u8 { 28enum class NCAContentType : u8 {
29 /// Executable-related data
28 Program = 0, 30 Program = 0,
31
32 /// Metadata.
29 Meta = 1, 33 Meta = 1,
34
35 /// Access control data.
30 Control = 2, 36 Control = 2,
37
38 /// Information related to the game manual
39 /// e.g. Legal information, etc.
31 Manual = 3, 40 Manual = 3,
41
42 /// System data.
32 Data = 4, 43 Data = 4,
33 Data_Unknown5 = 5, ///< Seems to be used on some system archives 44
45 /// Data that can be accessed by applications.
46 PublicData = 5,
34}; 47};
35 48
36enum class NCASectionCryptoType : u8 { 49enum class NCASectionCryptoType : u8 {
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/errors.h b/src/core/file_sys/errors.h
index e4a4ee4ab..bb4654366 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -11,6 +11,9 @@ namespace FileSys {
11constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1}; 11constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
12constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002}; 12constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
13constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001}; 13constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
14constexpr ResultCode ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005};
15constexpr ResultCode ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223};
16constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001};
14constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061}; 17constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
15constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062}; 18constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
16 19
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 61706966e..e11217708 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -7,6 +7,7 @@
7#include <cstddef> 7#include <cstddef>
8#include <cstring> 8#include <cstring>
9 9
10#include "common/file_util.h"
10#include "common/hex_util.h" 11#include "common/hex_util.h"
11#include "common/logging/log.h" 12#include "common/logging/log.h"
12#include "core/file_sys/content_archive.h" 13#include "core/file_sys/content_archive.h"
@@ -19,6 +20,7 @@
19#include "core/file_sys/vfs_vector.h" 20#include "core/file_sys/vfs_vector.h"
20#include "core/hle/service/filesystem/filesystem.h" 21#include "core/hle/service/filesystem/filesystem.h"
21#include "core/loader/loader.h" 22#include "core/loader/loader.h"
23#include "core/loader/nso.h"
22#include "core/settings.h" 24#include "core/settings.h"
23 25
24namespace FileSys { 26namespace FileSys {
@@ -31,14 +33,6 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
31 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", 33 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
32}; 34};
33 35
34struct NSOBuildHeader {
35 u32_le magic;
36 INSERT_PADDING_BYTES(0x3C);
37 std::array<u8, 0x20> build_id;
38 INSERT_PADDING_BYTES(0xA0);
39};
40static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size.");
41
42std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { 36std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
43 std::array<u8, sizeof(u32)> bytes{}; 37 std::array<u8, sizeof(u32)> bytes{};
44 bytes[0] = version % SINGLE_BYTE_MODULUS; 38 bytes[0] = version % SINGLE_BYTE_MODULUS;
@@ -162,14 +156,16 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
162} 156}
163 157
164std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { 158std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
165 if (nso.size() < 0x100) 159 if (nso.size() < sizeof(Loader::NSOHeader)) {
166 return nso; 160 return nso;
161 }
167 162
168 NSOBuildHeader header; 163 Loader::NSOHeader header;
169 std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader)); 164 std::memcpy(&header, nso.data(), sizeof(header));
170 165
171 if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 166 if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
172 return nso; 167 return nso;
168 }
173 169
174 const auto build_id_raw = Common::HexArrayToString(header.build_id); 170 const auto build_id_raw = Common::HexArrayToString(header.build_id);
175 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 171 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
@@ -212,9 +208,11 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
212 } 208 }
213 } 209 }
214 210
215 if (out.size() < 0x100) 211 if (out.size() < sizeof(Loader::NSOHeader)) {
216 return nso; 212 return nso;
217 std::memcpy(out.data(), &header, sizeof(NSOBuildHeader)); 213 }
214
215 std::memcpy(out.data(), &header, sizeof(header));
218 return out; 216 return out;
219} 217}
220 218
@@ -232,6 +230,57 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
232 return !CollectPatches(patch_dirs, build_id).empty(); 230 return !CollectPatches(patch_dirs, build_id).empty();
233} 231}
234 232
233static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id,
234 const std::array<u8, 0x20>& build_id_,
235 const VirtualDir& base_path, bool upper) {
236 const auto build_id_raw = Common::HexArrayToString(build_id_, upper);
237 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
238 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
239
240 if (file == nullptr) {
241 LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
242 title_id, build_id);
243 return std::nullopt;
244 }
245
246 std::vector<u8> data(file->GetSize());
247 if (file->Read(data.data(), data.size()) != data.size()) {
248 LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
249 title_id, build_id);
250 return std::nullopt;
251 }
252
253 TextCheatParser parser;
254 return parser.Parse(system, data);
255}
256
257std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
258 const std::array<u8, 32>& build_id_) const {
259 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
260 auto patch_dirs = load_dir->GetSubdirectories();
261 std::sort(patch_dirs.begin(), patch_dirs.end(),
262 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
263
264 std::vector<CheatList> out;
265 out.reserve(patch_dirs.size());
266 for (const auto& subdir : patch_dirs) {
267 auto cheats_dir = subdir->GetSubdirectory("cheats");
268 if (cheats_dir != nullptr) {
269 auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
270 if (res.has_value()) {
271 out.push_back(std::move(*res));
272 continue;
273 }
274
275 res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false);
276 if (res.has_value())
277 out.push_back(std::move(*res));
278 }
279 }
280
281 return out;
282}
283
235static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 284static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
236 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 285 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
237 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 286 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
@@ -403,6 +452,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
403 } 452 }
404 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) 453 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
405 AppendCommaIfNotEmpty(types, "LayeredFS"); 454 AppendCommaIfNotEmpty(types, "LayeredFS");
455 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("cheats")))
456 AppendCommaIfNotEmpty(types, "Cheats");
406 457
407 if (types.empty()) 458 if (types.empty())
408 continue; 459 continue;
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index b8a1652fd..de2672c76 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -8,9 +8,14 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/cheat_engine.h"
11#include "core/file_sys/nca_metadata.h" 12#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs.h"
13 14
15namespace Core {
16class System;
17}
18
14namespace FileSys { 19namespace FileSys {
15 20
16class NCA; 21class NCA;
@@ -45,6 +50,10 @@ public:
45 // Used to prevent expensive copies in NSO loader. 50 // Used to prevent expensive copies in NSO loader.
46 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; 51 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
47 52
53 // Creates a CheatList object with all
54 std::vector<CheatList> CreateCheatList(const Core::System& system,
55 const std::array<u8, 0x20>& build_id) const;
56
48 // Currently tracked RomFS patches: 57 // Currently tracked RomFS patches:
49 // - Game Updates 58 // - Game Updates
50 // - LayeredFS 59 // - LayeredFS
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 128199063..1c6bacace 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -94,7 +94,7 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
94 case NCAContentType::Control: 94 case NCAContentType::Control:
95 return ContentRecordType::Control; 95 return ContentRecordType::Control;
96 case NCAContentType::Data: 96 case NCAContentType::Data:
97 case NCAContentType::Data_Unknown5: 97 case NCAContentType::PublicData:
98 return ContentRecordType::Data; 98 return ContentRecordType::Data;
99 case NCAContentType::Manual: 99 case NCAContentType::Manual:
100 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. 100 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
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/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp
index e3e79f40a..c9722ed77 100644
--- a/src/core/file_sys/system_archive/system_archive.cpp
+++ b/src/core/file_sys/system_archive/system_archive.cpp
@@ -6,6 +6,7 @@
6#include "core/file_sys/romfs.h" 6#include "core/file_sys/romfs.h"
7#include "core/file_sys/system_archive/ng_word.h" 7#include "core/file_sys/system_archive/ng_word.h"
8#include "core/file_sys/system_archive/system_archive.h" 8#include "core/file_sys/system_archive/system_archive.h"
9#include "core/file_sys/system_archive/system_version.h"
9 10
10namespace FileSys::SystemArchive { 11namespace FileSys::SystemArchive {
11 12
@@ -30,7 +31,7 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI
30 {0x0100000000000806, "NgWord", &NgWord1}, 31 {0x0100000000000806, "NgWord", &NgWord1},
31 {0x0100000000000807, "SsidList", nullptr}, 32 {0x0100000000000807, "SsidList", nullptr},
32 {0x0100000000000808, "Dictionary", nullptr}, 33 {0x0100000000000808, "Dictionary", nullptr},
33 {0x0100000000000809, "SystemVersion", nullptr}, 34 {0x0100000000000809, "SystemVersion", &SystemVersion},
34 {0x010000000000080A, "AvatarImage", nullptr}, 35 {0x010000000000080A, "AvatarImage", nullptr},
35 {0x010000000000080B, "LocalNews", nullptr}, 36 {0x010000000000080B, "LocalNews", nullptr},
36 {0x010000000000080C, "Eula", nullptr}, 37 {0x010000000000080C, "Eula", nullptr},
diff --git a/src/core/file_sys/system_archive/system_version.cpp b/src/core/file_sys/system_archive/system_version.cpp
new file mode 100644
index 000000000..6e22f97b0
--- /dev/null
+++ b/src/core/file_sys/system_archive/system_version.cpp
@@ -0,0 +1,52 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/file_sys/system_archive/system_version.h"
6#include "core/file_sys/vfs_vector.h"
7
8namespace FileSys::SystemArchive {
9
10namespace SystemVersionData {
11
12// This section should reflect the best system version to describe yuzu's HLE api.
13// TODO(DarkLordZach): Update when HLE gets better.
14
15constexpr u8 VERSION_MAJOR = 5;
16constexpr u8 VERSION_MINOR = 1;
17constexpr u8 VERSION_MICRO = 0;
18
19constexpr u8 REVISION_MAJOR = 3;
20constexpr u8 REVISION_MINOR = 0;
21
22constexpr char PLATFORM_STRING[] = "NX";
23constexpr char VERSION_HASH[] = "23f9df53e25709d756e0c76effcb2473bd3447dd";
24constexpr char DISPLAY_VERSION[] = "5.1.0";
25constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 5.1.0-3.0";
26
27} // namespace SystemVersionData
28
29std::string GetLongDisplayVersion() {
30 return SystemVersionData::DISPLAY_TITLE;
31}
32
33VirtualDir SystemVersion() {
34 VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
35 file->WriteObject(SystemVersionData::VERSION_MAJOR, 0);
36 file->WriteObject(SystemVersionData::VERSION_MINOR, 1);
37 file->WriteObject(SystemVersionData::VERSION_MICRO, 2);
38 file->WriteObject(SystemVersionData::REVISION_MAJOR, 4);
39 file->WriteObject(SystemVersionData::REVISION_MINOR, 5);
40 file->WriteArray(SystemVersionData::PLATFORM_STRING,
41 std::min<u64>(sizeof(SystemVersionData::PLATFORM_STRING), 0x20ULL), 0x8);
42 file->WriteArray(SystemVersionData::VERSION_HASH,
43 std::min<u64>(sizeof(SystemVersionData::VERSION_HASH), 0x40ULL), 0x28);
44 file->WriteArray(SystemVersionData::DISPLAY_VERSION,
45 std::min<u64>(sizeof(SystemVersionData::DISPLAY_VERSION), 0x18ULL), 0x68);
46 file->WriteArray(SystemVersionData::DISPLAY_TITLE,
47 std::min<u64>(sizeof(SystemVersionData::DISPLAY_TITLE), 0x80ULL), 0x80);
48 return std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{file},
49 std::vector<VirtualDir>{}, "data");
50}
51
52} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/system_archive/system_version.h b/src/core/file_sys/system_archive/system_version.h
new file mode 100644
index 000000000..deed79b26
--- /dev/null
+++ b/src/core/file_sys/system_archive/system_version.h
@@ -0,0 +1,16 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include "core/file_sys/vfs_types.h"
9
10namespace FileSys::SystemArchive {
11
12std::string GetLongDisplayVersion();
13
14VirtualDir SystemVersion();
15
16} // namespace FileSys::SystemArchive
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index e29afd630..1320bbe77 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -30,7 +30,7 @@ private:
30 explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {} 30 explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
31 std::tuple<float, float, bool> GetStatus() const override { 31 std::tuple<float, float, bool> GetStatus() const override {
32 if (auto state = touch_state.lock()) { 32 if (auto state = touch_state.lock()) {
33 std::lock_guard<std::mutex> guard(state->mutex); 33 std::lock_guard guard{state->mutex};
34 return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed); 34 return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
35 } 35 }
36 return std::make_tuple(0.0f, 0.0f, false); 36 return std::make_tuple(0.0f, 0.0f, false);
@@ -81,7 +81,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
81 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) 81 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
82 return; 82 return;
83 83
84 std::lock_guard<std::mutex> guard(touch_state->mutex); 84 std::lock_guard guard{touch_state->mutex};
85 touch_state->touch_x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / 85 touch_state->touch_x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
86 (framebuffer_layout.screen.right - framebuffer_layout.screen.left); 86 (framebuffer_layout.screen.right - framebuffer_layout.screen.left);
87 touch_state->touch_y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / 87 touch_state->touch_y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
@@ -91,7 +91,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
91} 91}
92 92
93void EmuWindow::TouchReleased() { 93void EmuWindow::TouchReleased() {
94 std::lock_guard<std::mutex> guard(touch_state->mutex); 94 std::lock_guard guard{touch_state->mutex};
95 touch_state->touch_pressed = false; 95 touch_state->touch_pressed = false;
96 touch_state->touch_x = 0; 96 touch_state->touch_x = 0;
97 touch_state->touch_y = 0; 97 touch_state->touch_y = 0;
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 455d1f346..fae54bcc7 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -39,10 +39,10 @@ struct CommandHeader {
39 union { 39 union {
40 u32_le raw_low; 40 u32_le raw_low;
41 BitField<0, 16, CommandType> type; 41 BitField<0, 16, CommandType> type;
42 BitField<16, 4, u32_le> num_buf_x_descriptors; 42 BitField<16, 4, u32> num_buf_x_descriptors;
43 BitField<20, 4, u32_le> num_buf_a_descriptors; 43 BitField<20, 4, u32> num_buf_a_descriptors;
44 BitField<24, 4, u32_le> num_buf_b_descriptors; 44 BitField<24, 4, u32> num_buf_b_descriptors;
45 BitField<28, 4, u32_le> num_buf_w_descriptors; 45 BitField<28, 4, u32> num_buf_w_descriptors;
46 }; 46 };
47 47
48 enum class BufferDescriptorCFlag : u32 { 48 enum class BufferDescriptorCFlag : u32 {
@@ -53,28 +53,28 @@ struct CommandHeader {
53 53
54 union { 54 union {
55 u32_le raw_high; 55 u32_le raw_high;
56 BitField<0, 10, u32_le> data_size; 56 BitField<0, 10, u32> data_size;
57 BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags; 57 BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags;
58 BitField<31, 1, u32_le> enable_handle_descriptor; 58 BitField<31, 1, u32> enable_handle_descriptor;
59 }; 59 };
60}; 60};
61static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect"); 61static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect");
62 62
63union HandleDescriptorHeader { 63union HandleDescriptorHeader {
64 u32_le raw_high; 64 u32_le raw_high;
65 BitField<0, 1, u32_le> send_current_pid; 65 BitField<0, 1, u32> send_current_pid;
66 BitField<1, 4, u32_le> num_handles_to_copy; 66 BitField<1, 4, u32> num_handles_to_copy;
67 BitField<5, 4, u32_le> num_handles_to_move; 67 BitField<5, 4, u32> num_handles_to_move;
68}; 68};
69static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect"); 69static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect");
70 70
71struct BufferDescriptorX { 71struct BufferDescriptorX {
72 union { 72 union {
73 BitField<0, 6, u32_le> counter_bits_0_5; 73 BitField<0, 6, u32> counter_bits_0_5;
74 BitField<6, 3, u32_le> address_bits_36_38; 74 BitField<6, 3, u32> address_bits_36_38;
75 BitField<9, 3, u32_le> counter_bits_9_11; 75 BitField<9, 3, u32> counter_bits_9_11;
76 BitField<12, 4, u32_le> address_bits_32_35; 76 BitField<12, 4, u32> address_bits_32_35;
77 BitField<16, 16, u32_le> size; 77 BitField<16, 16, u32> size;
78 }; 78 };
79 79
80 u32_le address_bits_0_31; 80 u32_le address_bits_0_31;
@@ -103,10 +103,10 @@ struct BufferDescriptorABW {
103 u32_le address_bits_0_31; 103 u32_le address_bits_0_31;
104 104
105 union { 105 union {
106 BitField<0, 2, u32_le> flags; 106 BitField<0, 2, u32> flags;
107 BitField<2, 3, u32_le> address_bits_36_38; 107 BitField<2, 3, u32> address_bits_36_38;
108 BitField<24, 4, u32_le> size_bits_32_35; 108 BitField<24, 4, u32> size_bits_32_35;
109 BitField<28, 4, u32_le> address_bits_32_35; 109 BitField<28, 4, u32> address_bits_32_35;
110 }; 110 };
111 111
112 VAddr Address() const { 112 VAddr Address() const {
@@ -128,8 +128,8 @@ struct BufferDescriptorC {
128 u32_le address_bits_0_31; 128 u32_le address_bits_0_31;
129 129
130 union { 130 union {
131 BitField<0, 16, u32_le> address_bits_32_47; 131 BitField<0, 16, u32> address_bits_32_47;
132 BitField<16, 16, u32_le> size; 132 BitField<16, 16, u32> size;
133 }; 133 };
134 134
135 VAddr Address() const { 135 VAddr Address() const {
@@ -167,8 +167,8 @@ struct DomainMessageHeader {
167 struct { 167 struct {
168 union { 168 union {
169 BitField<0, 8, CommandType> command; 169 BitField<0, 8, CommandType> command;
170 BitField<8, 8, u32_le> input_object_count; 170 BitField<8, 8, u32> input_object_count;
171 BitField<16, 16, u32_le> size; 171 BitField<16, 16, u32> size;
172 }; 172 };
173 u32_le object_id; 173 u32_le object_id;
174 INSERT_PADDING_WORDS(2); 174 INSERT_PADDING_WORDS(2);
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index a1e4be070..68406eb63 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -275,6 +275,20 @@ inline void ResponseBuilder::Push(u64 value) {
275} 275}
276 276
277template <> 277template <>
278inline void ResponseBuilder::Push(float value) {
279 u32 integral;
280 std::memcpy(&integral, &value, sizeof(u32));
281 Push(integral);
282}
283
284template <>
285inline void ResponseBuilder::Push(double value) {
286 u64 integral;
287 std::memcpy(&integral, &value, sizeof(u64));
288 Push(integral);
289}
290
291template <>
278inline void ResponseBuilder::Push(bool value) { 292inline void ResponseBuilder::Push(bool value) {
279 Push(static_cast<u8>(value)); 293 Push(static_cast<u8>(value));
280} 294}
@@ -416,6 +430,22 @@ inline s64 RequestParser::Pop() {
416} 430}
417 431
418template <> 432template <>
433inline float RequestParser::Pop() {
434 const u32 value = Pop<u32>();
435 float real;
436 std::memcpy(&real, &value, sizeof(real));
437 return real;
438}
439
440template <>
441inline double RequestParser::Pop() {
442 const u64 value = Pop<u64>();
443 float real;
444 std::memcpy(&real, &value, sizeof(real));
445 return real;
446}
447
448template <>
419inline bool RequestParser::Pop() { 449inline bool RequestParser::Pop() {
420 return Pop<u8>() != 0; 450 return Pop<u8>() != 0;
421} 451}
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 352190da8..c8842410b 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -26,7 +26,7 @@ void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_
26 // them all. 26 // them all.
27 std::size_t last = waiting_threads.size(); 27 std::size_t last = waiting_threads.size();
28 if (num_to_wake > 0) { 28 if (num_to_wake > 0) {
29 last = num_to_wake; 29 last = std::min(last, static_cast<std::size_t>(num_to_wake));
30 } 30 }
31 31
32 // Signal the waiting threads. 32 // Signal the waiting threads.
@@ -90,9 +90,9 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
90 // Determine the modified value depending on the waiting count. 90 // Determine the modified value depending on the waiting count.
91 s32 updated_value; 91 s32 updated_value;
92 if (waiting_threads.empty()) { 92 if (waiting_threads.empty()) {
93 updated_value = value - 1;
94 } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
95 updated_value = value + 1; 93 updated_value = value + 1;
94 } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
95 updated_value = value - 1;
96 } else { 96 } else {
97 updated_value = value; 97 updated_value = value;
98 } 98 }
diff --git a/src/core/hle/kernel/code_set.cpp b/src/core/hle/kernel/code_set.cpp
new file mode 100644
index 000000000..1f434e9af
--- /dev/null
+++ b/src/core/hle/kernel/code_set.cpp
@@ -0,0 +1,12 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/kernel/code_set.h"
6
7namespace Kernel {
8
9CodeSet::CodeSet() = default;
10CodeSet::~CodeSet() = default;
11
12} // namespace Kernel
diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h
new file mode 100644
index 000000000..879957dcb
--- /dev/null
+++ b/src/core/hle/kernel/code_set.h
@@ -0,0 +1,89 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace Kernel {
13
14/**
15 * Represents executable data that may be loaded into a kernel process.
16 *
17 * A code set consists of three basic segments:
18 * - A code (AKA text) segment,
19 * - A read-only data segment (rodata)
20 * - A data segment
21 *
22 * The code segment is the portion of the object file that contains
23 * executable instructions.
24 *
25 * The read-only data segment in the portion of the object file that
26 * contains (as one would expect) read-only data, such as fixed constant
27 * values and data structures.
28 *
29 * The data segment is similar to the read-only data segment -- it contains
30 * variables and data structures that have predefined values, however,
31 * entities within this segment can be modified.
32 */
33struct CodeSet final {
34 /// A single segment within a code set.
35 struct Segment final {
36 /// The byte offset that this segment is located at.
37 std::size_t offset = 0;
38
39 /// The address to map this segment to.
40 VAddr addr = 0;
41
42 /// The size of this segment in bytes.
43 u32 size = 0;
44 };
45
46 explicit CodeSet();
47 ~CodeSet();
48
49 CodeSet(const CodeSet&) = delete;
50 CodeSet& operator=(const CodeSet&) = delete;
51
52 CodeSet(CodeSet&&) = default;
53 CodeSet& operator=(CodeSet&&) = default;
54
55 Segment& CodeSegment() {
56 return segments[0];
57 }
58
59 const Segment& CodeSegment() const {
60 return segments[0];
61 }
62
63 Segment& RODataSegment() {
64 return segments[1];
65 }
66
67 const Segment& RODataSegment() const {
68 return segments[1];
69 }
70
71 Segment& DataSegment() {
72 return segments[2];
73 }
74
75 const Segment& DataSegment() const {
76 return segments[2];
77 }
78
79 /// The overall data that backs this code set.
80 std::vector<u8> memory;
81
82 /// The segments that comprise this code set.
83 std::array<Segment, 3> segments;
84
85 /// The entry point address for this code set.
86 VAddr entrypoint = 0;
87};
88
89} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 4d224d01d..3f14bfa86 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -29,12 +29,12 @@ namespace Kernel {
29 * @param thread_handle The handle of the thread that's been awoken 29 * @param thread_handle The handle of the thread that's been awoken
30 * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time 30 * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
31 */ 31 */
32static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) { 32static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
33 const auto proper_handle = static_cast<Handle>(thread_handle); 33 const auto proper_handle = static_cast<Handle>(thread_handle);
34 const auto& system = Core::System::GetInstance(); 34 const auto& system = Core::System::GetInstance();
35 35
36 // Lock the global kernel mutex when we enter the kernel HLE. 36 // Lock the global kernel mutex when we enter the kernel HLE.
37 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 37 std::lock_guard lock{HLE::g_hle_lock};
38 38
39 SharedPtr<Thread> thread = 39 SharedPtr<Thread> thread =
40 system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle); 40 system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle);
@@ -62,7 +62,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
62 62
63 if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 || 63 if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 ||
64 thread->GetWaitHandle() != 0) { 64 thread->GetWaitHandle() != 0) {
65 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); 65 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex ||
66 thread->GetStatus() == ThreadStatus::WaitCondVar);
66 thread->SetMutexWaitAddress(0); 67 thread->SetMutexWaitAddress(0);
67 thread->SetCondVarWaitAddress(0); 68 thread->SetCondVarWaitAddress(0);
68 thread->SetWaitHandle(0); 69 thread->SetWaitHandle(0);
@@ -114,7 +115,7 @@ struct KernelCore::Impl {
114 115
115 // Creates the default system resource limit 116 // Creates the default system resource limit
116 void InitializeSystemResourceLimit(KernelCore& kernel) { 117 void InitializeSystemResourceLimit(KernelCore& kernel) {
117 system_resource_limit = ResourceLimit::Create(kernel, "System"); 118 system_resource_limit = ResourceLimit::Create(kernel);
118 119
119 // If setting the default system values fails, then something seriously wrong has occurred. 120 // If setting the default system values fails, then something seriously wrong has occurred.
120 ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000) 121 ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
@@ -190,6 +191,10 @@ const Process* KernelCore::CurrentProcess() const {
190 return impl->current_process; 191 return impl->current_process;
191} 192}
192 193
194const std::vector<SharedPtr<Process>>& KernelCore::GetProcessList() const {
195 return impl->process_list;
196}
197
193void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) { 198void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
194 impl->named_ports.emplace(std::move(name), std::move(port)); 199 impl->named_ports.emplace(std::move(name), std::move(port));
195} 200}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index ff17ff865..6b8738599 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -8,9 +8,6 @@
8#include <unordered_map> 8#include <unordered_map>
9#include "core/hle/kernel/object.h" 9#include "core/hle/kernel/object.h"
10 10
11template <typename T>
12class ResultVal;
13
14namespace Core { 11namespace Core {
15class System; 12class System;
16} 13}
@@ -75,6 +72,9 @@ public:
75 /// Retrieves a const pointer to the current process. 72 /// Retrieves a const pointer to the current process.
76 const Process* CurrentProcess() const; 73 const Process* CurrentProcess() const;
77 74
75 /// Retrieves the list of processes.
76 const std::vector<SharedPtr<Process>>& GetProcessList() const;
77
78 /// Adds a port to the named port table 78 /// Adds a port to the named port table
79 void AddNamedPort(std::string name, SharedPtr<ClientPort> port); 79 void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
80 80
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 0743670ad..98e87313b 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.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 <utility> 5#include <utility>
7#include <vector> 6#include <vector>
8 7
@@ -10,8 +9,11 @@
10#include "core/core.h" 9#include "core/core.h"
11#include "core/hle/kernel/errors.h" 10#include "core/hle/kernel/errors.h"
12#include "core/hle/kernel/handle_table.h" 11#include "core/hle/kernel/handle_table.h"
12#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/mutex.h" 13#include "core/hle/kernel/mutex.h"
14#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
15#include "core/hle/kernel/process.h"
16#include "core/hle/kernel/scheduler.h"
15#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
16#include "core/hle/result.h" 18#include "core/hle/result.h"
17#include "core/memory.h" 19#include "core/memory.h"
@@ -57,41 +59,47 @@ static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_t
57 } 59 }
58} 60}
59 61
60ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle holding_thread_handle, 62Mutex::Mutex(Core::System& system) : system{system} {}
63Mutex::~Mutex() = default;
64
65ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
61 Handle requesting_thread_handle) { 66 Handle requesting_thread_handle) {
62 // The mutex address must be 4-byte aligned 67 // The mutex address must be 4-byte aligned
63 if ((address % sizeof(u32)) != 0) { 68 if ((address % sizeof(u32)) != 0) {
64 return ERR_INVALID_ADDRESS; 69 return ERR_INVALID_ADDRESS;
65 } 70 }
66 71
72 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
73 Thread* const current_thread = system.CurrentScheduler().GetCurrentThread();
67 SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); 74 SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
68 SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle); 75 SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
69 76
70 // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another 77 // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
71 // thread. 78 // thread.
72 ASSERT(requesting_thread == GetCurrentThread()); 79 ASSERT(requesting_thread == current_thread);
73 80
74 u32 addr_value = Memory::Read32(address); 81 const u32 addr_value = Memory::Read32(address);
75 82
76 // If the mutex isn't being held, just return success. 83 // If the mutex isn't being held, just return success.
77 if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { 84 if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
78 return RESULT_SUCCESS; 85 return RESULT_SUCCESS;
79 } 86 }
80 87
81 if (holding_thread == nullptr) 88 if (holding_thread == nullptr) {
82 return ERR_INVALID_HANDLE; 89 return ERR_INVALID_HANDLE;
90 }
83 91
84 // Wait until the mutex is released 92 // Wait until the mutex is released
85 GetCurrentThread()->SetMutexWaitAddress(address); 93 current_thread->SetMutexWaitAddress(address);
86 GetCurrentThread()->SetWaitHandle(requesting_thread_handle); 94 current_thread->SetWaitHandle(requesting_thread_handle);
87 95
88 GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex); 96 current_thread->SetStatus(ThreadStatus::WaitMutex);
89 GetCurrentThread()->InvalidateWakeupCallback(); 97 current_thread->InvalidateWakeupCallback();
90 98
91 // Update the lock holder thread's priority to prevent priority inversion. 99 // Update the lock holder thread's priority to prevent priority inversion.
92 holding_thread->AddMutexWaiter(GetCurrentThread()); 100 holding_thread->AddMutexWaiter(current_thread);
93 101
94 Core::System::GetInstance().PrepareReschedule(); 102 system.PrepareReschedule();
95 103
96 return RESULT_SUCCESS; 104 return RESULT_SUCCESS;
97} 105}
@@ -102,7 +110,8 @@ ResultCode Mutex::Release(VAddr address) {
102 return ERR_INVALID_ADDRESS; 110 return ERR_INVALID_ADDRESS;
103 } 111 }
104 112
105 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); 113 auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
114 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
106 115
107 // There are no more threads waiting for the mutex, release it completely. 116 // There are no more threads waiting for the mutex, release it completely.
108 if (thread == nullptr) { 117 if (thread == nullptr) {
@@ -111,7 +120,7 @@ ResultCode Mutex::Release(VAddr address) {
111 } 120 }
112 121
113 // Transfer the ownership of the mutex from the previous owner to the new one. 122 // Transfer the ownership of the mutex from the previous owner to the new one.
114 TransferMutexOwnership(address, GetCurrentThread(), thread); 123 TransferMutexOwnership(address, current_thread, thread);
115 124
116 u32 mutex_value = thread->GetWaitHandle(); 125 u32 mutex_value = thread->GetWaitHandle();
117 126
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 81e62d497..b904de2e8 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -5,32 +5,34 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/object.h"
9 8
10union ResultCode; 9union ResultCode;
11 10
12namespace Kernel { 11namespace Core {
12class System;
13}
13 14
14class HandleTable; 15namespace Kernel {
15class Thread;
16 16
17class Mutex final { 17class Mutex final {
18public: 18public:
19 explicit Mutex(Core::System& system);
20 ~Mutex();
21
19 /// Flag that indicates that a mutex still has threads waiting for it. 22 /// Flag that indicates that a mutex still has threads waiting for it.
20 static constexpr u32 MutexHasWaitersFlag = 0x40000000; 23 static constexpr u32 MutexHasWaitersFlag = 0x40000000;
21 /// Mask of the bits in a mutex address value that contain the mutex owner. 24 /// Mask of the bits in a mutex address value that contain the mutex owner.
22 static constexpr u32 MutexOwnerMask = 0xBFFFFFFF; 25 static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
23 26
24 /// Attempts to acquire a mutex at the specified address. 27 /// Attempts to acquire a mutex at the specified address.
25 static ResultCode TryAcquire(HandleTable& handle_table, VAddr address, 28 ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
26 Handle holding_thread_handle, Handle requesting_thread_handle); 29 Handle requesting_thread_handle);
27 30
28 /// Releases the mutex at the specified address. 31 /// Releases the mutex at the specified address.
29 static ResultCode Release(VAddr address); 32 ResultCode Release(VAddr address);
30 33
31private: 34private:
32 Mutex() = default; 35 Core::System& system;
33 ~Mutex() = default;
34}; 36};
35 37
36} // namespace Kernel 38} // namespace Kernel
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index 8870463d0..10431e94c 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -23,7 +23,7 @@ bool Object::IsWaitable() const {
23 case HandleType::Unknown: 23 case HandleType::Unknown:
24 case HandleType::WritableEvent: 24 case HandleType::WritableEvent:
25 case HandleType::SharedMemory: 25 case HandleType::SharedMemory:
26 case HandleType::AddressArbiter: 26 case HandleType::TransferMemory:
27 case HandleType::ResourceLimit: 27 case HandleType::ResourceLimit:
28 case HandleType::ClientPort: 28 case HandleType::ClientPort:
29 case HandleType::ClientSession: 29 case HandleType::ClientSession:
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index 4c2505908..332876c27 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -22,9 +22,9 @@ enum class HandleType : u32 {
22 WritableEvent, 22 WritableEvent,
23 ReadableEvent, 23 ReadableEvent,
24 SharedMemory, 24 SharedMemory,
25 TransferMemory,
25 Thread, 26 Thread,
26 Process, 27 Process,
27 AddressArbiter,
28 ResourceLimit, 28 ResourceLimit,
29 ClientPort, 29 ClientPort,
30 ServerPort, 30 ServerPort,
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 49fced7b1..041267318 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -5,10 +5,12 @@
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <random> 7#include <random>
8#include "common/alignment.h"
8#include "common/assert.h" 9#include "common/assert.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "core/core.h" 11#include "core/core.h"
11#include "core/file_sys/program_metadata.h" 12#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/code_set.h"
12#include "core/hle/kernel/errors.h" 14#include "core/hle/kernel/errors.h"
13#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 16#include "core/hle/kernel/process.h"
@@ -31,7 +33,7 @@ namespace {
31 */ 33 */
32void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) { 34void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
33 // Setup page table so we can write to memory 35 // Setup page table so we can write to memory
34 SetCurrentPageTable(&owner_process.VMManager().page_table); 36 Memory::SetCurrentPageTable(&owner_process.VMManager().page_table);
35 37
36 // Initialize new "main" thread 38 // Initialize new "main" thread
37 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); 39 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
@@ -50,9 +52,6 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_poi
50} 52}
51} // Anonymous namespace 53} // Anonymous namespace
52 54
53CodeSet::CodeSet() = default;
54CodeSet::~CodeSet() = default;
55
56SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) { 55SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
57 auto& kernel = system.Kernel(); 56 auto& kernel = system.Kernel();
58 57
@@ -77,6 +76,18 @@ SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
77 return resource_limit; 76 return resource_limit;
78} 77}
79 78
79u64 Process::GetTotalPhysicalMemoryUsed() const {
80 return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
81}
82
83void Process::RegisterThread(const Thread* thread) {
84 thread_list.push_back(thread);
85}
86
87void Process::UnregisterThread(const Thread* thread) {
88 thread_list.remove(thread);
89}
90
80ResultCode Process::ClearSignalState() { 91ResultCode Process::ClearSignalState() {
81 if (status == ProcessStatus::Exited) { 92 if (status == ProcessStatus::Exited) {
82 LOG_ERROR(Kernel, "called on a terminated process instance."); 93 LOG_ERROR(Kernel, "called on a terminated process instance.");
@@ -109,14 +120,17 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
109 return handle_table.SetSize(capabilities.GetHandleTableSize()); 120 return handle_table.SetSize(capabilities.GetHandleTableSize());
110} 121}
111 122
112void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { 123void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) {
124 // The kernel always ensures that the given stack size is page aligned.
125 main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
126
113 // Allocate and map the main thread stack 127 // Allocate and map the main thread stack
114 // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part 128 // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
115 // of the user address space. 129 // of the user address space.
130 const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
116 vm_manager 131 vm_manager
117 .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size, 132 .MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size),
118 std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, 133 0, main_thread_stack_size, MemoryState::Stack)
119 MemoryState::Stack)
120 .Unwrap(); 134 .Unwrap();
121 135
122 vm_manager.LogLayout(); 136 vm_manager.LogLayout();
@@ -212,33 +226,38 @@ void Process::FreeTLSSlot(VAddr tls_address) {
212} 226}
213 227
214void Process::LoadModule(CodeSet module_, VAddr base_addr) { 228void Process::LoadModule(CodeSet module_, VAddr base_addr) {
215 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, 229 const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory));
230
231 const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
216 MemoryState memory_state) { 232 MemoryState memory_state) {
217 const auto vma = vm_manager 233 const auto vma = vm_manager
218 .MapMemoryBlock(segment.addr + base_addr, module_.memory, 234 .MapMemoryBlock(segment.addr + base_addr, memory, segment.offset,
219 segment.offset, segment.size, memory_state) 235 segment.size, memory_state)
220 .Unwrap(); 236 .Unwrap();
221 vm_manager.Reprotect(vma, permissions); 237 vm_manager.Reprotect(vma, permissions);
222 }; 238 };
223 239
224 // Map CodeSet segments 240 // Map CodeSet segments
225 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); 241 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code);
226 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); 242 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData);
227 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 243 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
244
245 code_memory_size += module_.memory.size();
228 246
229 // Clear instruction cache in CPU JIT 247 // Clear instruction cache in CPU JIT
230 system.InvalidateCpuInstructionCaches(); 248 system.InvalidateCpuInstructionCaches();
231} 249}
232 250
233Process::Process(Core::System& system) 251Process::Process(Core::System& system)
234 : WaitObject{system.Kernel()}, address_arbiter{system}, system{system} {} 252 : WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
253
235Process::~Process() = default; 254Process::~Process() = default;
236 255
237void Process::Acquire(Thread* thread) { 256void Process::Acquire(Thread* thread) {
238 ASSERT_MSG(!ShouldWait(thread), "Object unavailable!"); 257 ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
239} 258}
240 259
241bool Process::ShouldWait(Thread* thread) const { 260bool Process::ShouldWait(const Thread* thread) const {
242 return !is_signaled; 261 return !is_signaled;
243} 262}
244 263
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 47ffd4ad3..f060f2a3b 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -7,13 +7,14 @@
7#include <array> 7#include <array>
8#include <bitset> 8#include <bitset>
9#include <cstddef> 9#include <cstddef>
10#include <memory> 10#include <list>
11#include <string> 11#include <string>
12#include <vector> 12#include <vector>
13#include <boost/container/static_vector.hpp> 13#include <boost/container/static_vector.hpp>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "core/hle/kernel/address_arbiter.h" 15#include "core/hle/kernel/address_arbiter.h"
16#include "core/hle/kernel/handle_table.h" 16#include "core/hle/kernel/handle_table.h"
17#include "core/hle/kernel/mutex.h"
17#include "core/hle/kernel/process_capability.h" 18#include "core/hle/kernel/process_capability.h"
18#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
19#include "core/hle/kernel/wait_object.h" 20#include "core/hle/kernel/wait_object.h"
@@ -33,13 +34,7 @@ class KernelCore;
33class ResourceLimit; 34class ResourceLimit;
34class Thread; 35class Thread;
35 36
36struct AddressMapping { 37struct CodeSet;
37 // Address and size must be page-aligned
38 VAddr address;
39 u64 size;
40 bool read_only;
41 bool unk_flag;
42};
43 38
44enum class MemoryRegion : u16 { 39enum class MemoryRegion : u16 {
45 APPLICATION = 1, 40 APPLICATION = 1,
@@ -65,46 +60,6 @@ enum class ProcessStatus {
65 DebugBreak, 60 DebugBreak,
66}; 61};
67 62
68struct CodeSet final {
69 struct Segment {
70 std::size_t offset = 0;
71 VAddr addr = 0;
72 u32 size = 0;
73 };
74
75 explicit CodeSet();
76 ~CodeSet();
77
78 Segment& CodeSegment() {
79 return segments[0];
80 }
81
82 const Segment& CodeSegment() const {
83 return segments[0];
84 }
85
86 Segment& RODataSegment() {
87 return segments[1];
88 }
89
90 const Segment& RODataSegment() const {
91 return segments[1];
92 }
93
94 Segment& DataSegment() {
95 return segments[2];
96 }
97
98 const Segment& DataSegment() const {
99 return segments[2];
100 }
101
102 std::shared_ptr<std::vector<u8>> memory;
103
104 std::array<Segment, 3> segments;
105 VAddr entrypoint = 0;
106};
107
108class Process final : public WaitObject { 63class Process final : public WaitObject {
109public: 64public:
110 enum : u64 { 65 enum : u64 {
@@ -165,6 +120,16 @@ public:
165 return address_arbiter; 120 return address_arbiter;
166 } 121 }
167 122
123 /// Gets a reference to the process' mutex lock.
124 Mutex& GetMutex() {
125 return mutex;
126 }
127
128 /// Gets a const reference to the process' mutex lock
129 const Mutex& GetMutex() const {
130 return mutex;
131 }
132
168 /// Gets the current status of the process 133 /// Gets the current status of the process
169 ProcessStatus GetStatus() const { 134 ProcessStatus GetStatus() const {
170 return status; 135 return status;
@@ -222,6 +187,22 @@ public:
222 return random_entropy.at(index); 187 return random_entropy.at(index);
223 } 188 }
224 189
190 /// Retrieves the total physical memory used by this process in bytes.
191 u64 GetTotalPhysicalMemoryUsed() const;
192
193 /// Gets the list of all threads created with this process as their owner.
194 const std::list<const Thread*>& GetThreadList() const {
195 return thread_list;
196 }
197
198 /// Registers a thread as being created under this process,
199 /// adding it to this process' thread list.
200 void RegisterThread(const Thread* thread);
201
202 /// Unregisters a thread from this process, removing it
203 /// from this process' thread list.
204 void UnregisterThread(const Thread* thread);
205
225 /// Clears the signaled state of the process if and only if it's signaled. 206 /// Clears the signaled state of the process if and only if it's signaled.
226 /// 207 ///
227 /// @pre The process must not be already terminated. If this is called on a 208 /// @pre The process must not be already terminated. If this is called on a
@@ -246,7 +227,7 @@ public:
246 /** 227 /**
247 * Applies address space changes and launches the process main thread. 228 * Applies address space changes and launches the process main thread.
248 */ 229 */
249 void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); 230 void Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size);
250 231
251 /** 232 /**
252 * Prepares a process for termination by stopping all of its threads 233 * Prepares a process for termination by stopping all of its threads
@@ -270,7 +251,7 @@ private:
270 ~Process() override; 251 ~Process() override;
271 252
272 /// Checks if the specified thread should wait until this process is available. 253 /// Checks if the specified thread should wait until this process is available.
273 bool ShouldWait(Thread* thread) const override; 254 bool ShouldWait(const Thread* thread) const override;
274 255
275 /// Acquires/locks this process for the specified thread if it's available. 256 /// Acquires/locks this process for the specified thread if it's available.
276 void Acquire(Thread* thread) override; 257 void Acquire(Thread* thread) override;
@@ -283,6 +264,12 @@ private:
283 /// Memory manager for this process. 264 /// Memory manager for this process.
284 Kernel::VMManager vm_manager; 265 Kernel::VMManager vm_manager;
285 266
267 /// Size of the main thread's stack in bytes.
268 u64 main_thread_stack_size = 0;
269
270 /// Size of the loaded code memory in bytes.
271 u64 code_memory_size = 0;
272
286 /// Current status of the process 273 /// Current status of the process
287 ProcessStatus status; 274 ProcessStatus status;
288 275
@@ -327,9 +314,17 @@ private:
327 /// Per-process address arbiter. 314 /// Per-process address arbiter.
328 AddressArbiter address_arbiter; 315 AddressArbiter address_arbiter;
329 316
317 /// The per-process mutex lock instance used for handling various
318 /// forms of services, such as lock arbitration, and condition
319 /// variable related facilities.
320 Mutex mutex;
321
330 /// Random values for svcGetInfo RandomEntropy 322 /// Random values for svcGetInfo RandomEntropy
331 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; 323 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
332 324
325 /// List of threads that are running with this process as their owner.
326 std::list<const Thread*> thread_list;
327
333 /// System context 328 /// System context
334 Core::System& system; 329 Core::System& system;
335 330
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp
index 0e5083f70..c2b798a4e 100644
--- a/src/core/hle/kernel/readable_event.cpp
+++ b/src/core/hle/kernel/readable_event.cpp
@@ -14,7 +14,7 @@ namespace Kernel {
14ReadableEvent::ReadableEvent(KernelCore& kernel) : WaitObject{kernel} {} 14ReadableEvent::ReadableEvent(KernelCore& kernel) : WaitObject{kernel} {}
15ReadableEvent::~ReadableEvent() = default; 15ReadableEvent::~ReadableEvent() = default;
16 16
17bool ReadableEvent::ShouldWait(Thread* thread) const { 17bool ReadableEvent::ShouldWait(const Thread* thread) const {
18 return !signaled; 18 return !signaled;
19} 19}
20 20
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h
index 77a9c362c..2eb9dcbb7 100644
--- a/src/core/hle/kernel/readable_event.h
+++ b/src/core/hle/kernel/readable_event.h
@@ -36,7 +36,7 @@ public:
36 return HANDLE_TYPE; 36 return HANDLE_TYPE;
37 } 37 }
38 38
39 bool ShouldWait(Thread* thread) const override; 39 bool ShouldWait(const Thread* thread) const override;
40 void Acquire(Thread* thread) override; 40 void Acquire(Thread* thread) override;
41 41
42 /// Unconditionally clears the readable event's state. 42 /// Unconditionally clears the readable event's state.
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index 2f9695005..173f69915 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -16,11 +16,8 @@ constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
16ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {} 16ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
17ResourceLimit::~ResourceLimit() = default; 17ResourceLimit::~ResourceLimit() = default;
18 18
19SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string name) { 19SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {
20 SharedPtr<ResourceLimit> resource_limit(new ResourceLimit(kernel)); 20 return new ResourceLimit(kernel);
21
22 resource_limit->name = std::move(name);
23 return resource_limit;
24} 21}
25 22
26s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { 23s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 59dc11c22..70e09858a 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -31,16 +31,14 @@ constexpr bool IsValidResourceType(ResourceType type) {
31 31
32class ResourceLimit final : public Object { 32class ResourceLimit final : public Object {
33public: 33public:
34 /** 34 /// Creates a resource limit object.
35 * Creates a resource limit object. 35 static SharedPtr<ResourceLimit> Create(KernelCore& kernel);
36 */
37 static SharedPtr<ResourceLimit> Create(KernelCore& kernel, std::string name = "Unknown");
38 36
39 std::string GetTypeName() const override { 37 std::string GetTypeName() const override {
40 return "ResourceLimit"; 38 return "ResourceLimit";
41 } 39 }
42 std::string GetName() const override { 40 std::string GetName() const override {
43 return name; 41 return GetTypeName();
44 } 42 }
45 43
46 static const HandleType HANDLE_TYPE = HandleType::ResourceLimit; 44 static const HandleType HANDLE_TYPE = HandleType::ResourceLimit;
@@ -95,9 +93,6 @@ private:
95 ResourceArray limits{}; 93 ResourceArray limits{};
96 /// Current resource limit values. 94 /// Current resource limit values.
97 ResourceArray values{}; 95 ResourceArray values{};
98
99 /// Name of resource limit object.
100 std::string name;
101}; 96};
102 97
103} // namespace Kernel 98} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 5fccfd9f4..ac501bf7f 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -29,8 +29,8 @@ Scheduler::~Scheduler() {
29} 29}
30 30
31bool Scheduler::HaveReadyThreads() const { 31bool Scheduler::HaveReadyThreads() const {
32 std::lock_guard<std::mutex> lock(scheduler_mutex); 32 std::lock_guard lock{scheduler_mutex};
33 return ready_queue.get_first() != nullptr; 33 return !ready_queue.empty();
34} 34}
35 35
36Thread* Scheduler::GetCurrentThread() const { 36Thread* Scheduler::GetCurrentThread() const {
@@ -46,22 +46,27 @@ Thread* Scheduler::PopNextReadyThread() {
46 Thread* thread = GetCurrentThread(); 46 Thread* thread = GetCurrentThread();
47 47
48 if (thread && thread->GetStatus() == ThreadStatus::Running) { 48 if (thread && thread->GetStatus() == ThreadStatus::Running) {
49 if (ready_queue.empty()) {
50 return thread;
51 }
49 // We have to do better than the current thread. 52 // We have to do better than the current thread.
50 // This call returns null when that's not possible. 53 // This call returns null when that's not possible.
51 next = ready_queue.pop_first_better(thread->GetPriority()); 54 next = ready_queue.front();
52 if (!next) { 55 if (next == nullptr || next->GetPriority() >= thread->GetPriority()) {
53 // Otherwise just keep going with the current thread
54 next = thread; 56 next = thread;
55 } 57 }
56 } else { 58 } else {
57 next = ready_queue.pop_first(); 59 if (ready_queue.empty()) {
60 return nullptr;
61 }
62 next = ready_queue.front();
58 } 63 }
59 64
60 return next; 65 return next;
61} 66}
62 67
63void Scheduler::SwitchContext(Thread* new_thread) { 68void Scheduler::SwitchContext(Thread* new_thread) {
64 Thread* const previous_thread = GetCurrentThread(); 69 Thread* previous_thread = GetCurrentThread();
65 Process* const previous_process = system.Kernel().CurrentProcess(); 70 Process* const previous_process = system.Kernel().CurrentProcess();
66 71
67 UpdateLastContextSwitchTime(previous_thread, previous_process); 72 UpdateLastContextSwitchTime(previous_thread, previous_process);
@@ -75,7 +80,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
75 if (previous_thread->GetStatus() == ThreadStatus::Running) { 80 if (previous_thread->GetStatus() == ThreadStatus::Running) {
76 // This is only the case when a reschedule is triggered without the current thread 81 // This is only the case when a reschedule is triggered without the current thread
77 // yielding execution (i.e. an event triggered, system core time-sliced, etc) 82 // yielding execution (i.e. an event triggered, system core time-sliced, etc)
78 ready_queue.push_front(previous_thread->GetPriority(), previous_thread); 83 ready_queue.add(previous_thread, previous_thread->GetPriority(), false);
79 previous_thread->SetStatus(ThreadStatus::Ready); 84 previous_thread->SetStatus(ThreadStatus::Ready);
80 } 85 }
81 } 86 }
@@ -90,13 +95,13 @@ void Scheduler::SwitchContext(Thread* new_thread) {
90 95
91 current_thread = new_thread; 96 current_thread = new_thread;
92 97
93 ready_queue.remove(new_thread->GetPriority(), new_thread); 98 ready_queue.remove(new_thread, new_thread->GetPriority());
94 new_thread->SetStatus(ThreadStatus::Running); 99 new_thread->SetStatus(ThreadStatus::Running);
95 100
96 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 101 auto* const thread_owner_process = current_thread->GetOwnerProcess();
97 if (previous_process != thread_owner_process) { 102 if (previous_process != thread_owner_process) {
98 system.Kernel().MakeCurrentProcess(thread_owner_process); 103 system.Kernel().MakeCurrentProcess(thread_owner_process);
99 SetCurrentPageTable(&thread_owner_process->VMManager().page_table); 104 Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
100 } 105 }
101 106
102 cpu_core.LoadContext(new_thread->GetContext()); 107 cpu_core.LoadContext(new_thread->GetContext());
@@ -127,7 +132,7 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
127} 132}
128 133
129void Scheduler::Reschedule() { 134void Scheduler::Reschedule() {
130 std::lock_guard<std::mutex> lock(scheduler_mutex); 135 std::lock_guard lock{scheduler_mutex};
131 136
132 Thread* cur = GetCurrentThread(); 137 Thread* cur = GetCurrentThread();
133 Thread* next = PopNextReadyThread(); 138 Thread* next = PopNextReadyThread();
@@ -143,51 +148,54 @@ void Scheduler::Reschedule() {
143 SwitchContext(next); 148 SwitchContext(next);
144} 149}
145 150
146void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) { 151void Scheduler::AddThread(SharedPtr<Thread> thread) {
147 std::lock_guard<std::mutex> lock(scheduler_mutex); 152 std::lock_guard lock{scheduler_mutex};
148 153
149 thread_list.push_back(std::move(thread)); 154 thread_list.push_back(std::move(thread));
150 ready_queue.prepare(priority);
151} 155}
152 156
153void Scheduler::RemoveThread(Thread* thread) { 157void Scheduler::RemoveThread(Thread* thread) {
154 std::lock_guard<std::mutex> lock(scheduler_mutex); 158 std::lock_guard lock{scheduler_mutex};
155 159
156 thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), 160 thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
157 thread_list.end()); 161 thread_list.end());
158} 162}
159 163
160void Scheduler::ScheduleThread(Thread* thread, u32 priority) { 164void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
161 std::lock_guard<std::mutex> lock(scheduler_mutex); 165 std::lock_guard lock{scheduler_mutex};
162 166
163 ASSERT(thread->GetStatus() == ThreadStatus::Ready); 167 ASSERT(thread->GetStatus() == ThreadStatus::Ready);
164 ready_queue.push_back(priority, thread); 168 ready_queue.add(thread, priority);
165} 169}
166 170
167void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { 171void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
168 std::lock_guard<std::mutex> lock(scheduler_mutex); 172 std::lock_guard lock{scheduler_mutex};
169 173
170 ASSERT(thread->GetStatus() == ThreadStatus::Ready); 174 ASSERT(thread->GetStatus() == ThreadStatus::Ready);
171 ready_queue.remove(priority, thread); 175 ready_queue.remove(thread, priority);
172} 176}
173 177
174void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { 178void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
175 std::lock_guard<std::mutex> lock(scheduler_mutex); 179 std::lock_guard lock{scheduler_mutex};
180 if (thread->GetPriority() == priority) {
181 return;
182 }
176 183
177 // If thread was ready, adjust queues 184 // If thread was ready, adjust queues
178 if (thread->GetStatus() == ThreadStatus::Ready) 185 if (thread->GetStatus() == ThreadStatus::Ready)
179 ready_queue.move(thread, thread->GetPriority(), priority); 186 ready_queue.adjust(thread, thread->GetPriority(), priority);
180 else
181 ready_queue.prepare(priority);
182} 187}
183 188
184Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const { 189Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
185 std::lock_guard<std::mutex> lock(scheduler_mutex); 190 std::lock_guard lock{scheduler_mutex};
186 191
187 const u32 mask = 1U << core; 192 const u32 mask = 1U << core;
188 return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) { 193 for (auto* thread : ready_queue) {
189 return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority; 194 if ((thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority) {
190 }); 195 return thread;
196 }
197 }
198 return nullptr;
191} 199}
192 200
193void Scheduler::YieldWithoutLoadBalancing(Thread* thread) { 201void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
@@ -199,8 +207,7 @@ void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
199 ASSERT(thread->GetPriority() < THREADPRIO_COUNT); 207 ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
200 208
201 // Yield this thread -- sleep for zero time and force reschedule to different thread 209 // Yield this thread -- sleep for zero time and force reschedule to different thread
202 WaitCurrentThread_Sleep(); 210 GetCurrentThread()->Sleep(0);
203 GetCurrentThread()->WakeAfterDelay(0);
204} 211}
205 212
206void Scheduler::YieldWithLoadBalancing(Thread* thread) { 213void Scheduler::YieldWithLoadBalancing(Thread* thread) {
@@ -215,8 +222,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
215 ASSERT(priority < THREADPRIO_COUNT); 222 ASSERT(priority < THREADPRIO_COUNT);
216 223
217 // Sleep for zero time to be able to force reschedule to different thread 224 // Sleep for zero time to be able to force reschedule to different thread
218 WaitCurrentThread_Sleep(); 225 GetCurrentThread()->Sleep(0);
219 GetCurrentThread()->WakeAfterDelay(0);
220 226
221 Thread* suggested_thread = nullptr; 227 Thread* suggested_thread = nullptr;
222 228
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 1c5bf57d9..b29bf7be8 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -7,7 +7,7 @@
7#include <mutex> 7#include <mutex>
8#include <vector> 8#include <vector>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/thread_queue_list.h" 10#include "common/multi_level_queue.h"
11#include "core/hle/kernel/object.h" 11#include "core/hle/kernel/object.h"
12#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
13 13
@@ -38,7 +38,7 @@ public:
38 u64 GetLastContextSwitchTicks() const; 38 u64 GetLastContextSwitchTicks() const;
39 39
40 /// Adds a new thread to the scheduler 40 /// Adds a new thread to the scheduler
41 void AddThread(SharedPtr<Thread> thread, u32 priority); 41 void AddThread(SharedPtr<Thread> thread);
42 42
43 /// Removes a thread from the scheduler 43 /// Removes a thread from the scheduler
44 void RemoveThread(Thread* thread); 44 void RemoveThread(Thread* thread);
@@ -156,7 +156,7 @@ private:
156 std::vector<SharedPtr<Thread>> thread_list; 156 std::vector<SharedPtr<Thread>> thread_list;
157 157
158 /// Lists only ready thread ids. 158 /// Lists only ready thread ids.
159 Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue; 159 Common::MultiLevelQueue<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
160 160
161 SharedPtr<Thread> current_thread = nullptr; 161 SharedPtr<Thread> current_thread = nullptr;
162 162
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 0e1515c89..708fdf9e1 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -30,7 +30,7 @@ void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session)
30 pending_sessions.push_back(std::move(pending_session)); 30 pending_sessions.push_back(std::move(pending_session));
31} 31}
32 32
33bool ServerPort::ShouldWait(Thread* thread) const { 33bool ServerPort::ShouldWait(const Thread* thread) const {
34 // If there are no pending sessions, we wait until a new one is added. 34 // If there are no pending sessions, we wait until a new one is added.
35 return pending_sessions.empty(); 35 return pending_sessions.empty();
36} 36}
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index 9bc667cf2..76293cb8b 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -75,7 +75,7 @@ public:
75 /// waiting to be accepted by this port. 75 /// waiting to be accepted by this port.
76 void AppendPendingSession(SharedPtr<ServerSession> pending_session); 76 void AppendPendingSession(SharedPtr<ServerSession> pending_session);
77 77
78 bool ShouldWait(Thread* thread) const override; 78 bool ShouldWait(const Thread* thread) const override;
79 void Acquire(Thread* thread) override; 79 void Acquire(Thread* thread) override;
80 80
81private: 81private:
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 4d8a337a7..40cec143e 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -46,7 +46,7 @@ ResultVal<SharedPtr<ServerSession>> ServerSession::Create(KernelCore& kernel, st
46 return MakeResult(std::move(server_session)); 46 return MakeResult(std::move(server_session));
47} 47}
48 48
49bool ServerSession::ShouldWait(Thread* thread) const { 49bool ServerSession::ShouldWait(const Thread* thread) const {
50 // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. 50 // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
51 if (parent->client == nullptr) 51 if (parent->client == nullptr)
52 return false; 52 return false;
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index aea4ccfeb..79b84bade 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -82,7 +82,7 @@ public:
82 */ 82 */
83 ResultCode HandleSyncRequest(SharedPtr<Thread> thread); 83 ResultCode HandleSyncRequest(SharedPtr<Thread> thread);
84 84
85 bool ShouldWait(Thread* thread) const override; 85 bool ShouldWait(const Thread* thread) const override;
86 86
87 void Acquire(Thread* thread) override; 87 void Acquire(Thread* thread) override;
88 88
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 62861da36..f15c5ee36 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -9,7 +9,6 @@
9#include "core/hle/kernel/errors.h" 9#include "core/hle/kernel/errors.h"
10#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/shared_memory.h" 11#include "core/hle/kernel/shared_memory.h"
12#include "core/memory.h"
13 12
14namespace Kernel { 13namespace Kernel {
15 14
@@ -119,7 +118,15 @@ ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermi
119 ConvertPermissions(permissions)); 118 ConvertPermissions(permissions));
120} 119}
121 120
122ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) { 121ResultCode SharedMemory::Unmap(Process& target_process, VAddr address, u64 unmap_size) {
122 if (unmap_size != size) {
123 LOG_ERROR(Kernel,
124 "Invalid size passed to Unmap. Size must be equal to the size of the "
125 "memory managed. Shared memory size=0x{:016X}, Unmap size=0x{:016X}",
126 size, unmap_size);
127 return ERR_INVALID_SIZE;
128 }
129
123 // TODO(Subv): Verify what happens if the application tries to unmap an address that is not 130 // TODO(Subv): Verify what happens if the application tries to unmap an address that is not
124 // mapped to a SharedMemory. 131 // mapped to a SharedMemory.
125 return target_process.VMManager().UnmapRange(address, size); 132 return target_process.VMManager().UnmapRange(address, size);
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index dab2a6bea..37e18c443 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -104,11 +104,17 @@ public:
104 104
105 /** 105 /**
106 * Unmaps a shared memory block from the specified address in system memory 106 * Unmaps a shared memory block from the specified address in system memory
107 *
107 * @param target_process Process from which to unmap the memory block. 108 * @param target_process Process from which to unmap the memory block.
108 * @param address Address in system memory where the shared memory block is mapped 109 * @param address Address in system memory where the shared memory block is mapped.
110 * @param unmap_size The amount of bytes to unmap from this shared memory instance.
111 *
109 * @return Result code of the unmap operation 112 * @return Result code of the unmap operation
113 *
114 * @pre The given size to unmap must be the same size as the amount of memory managed by
115 * the SharedMemory instance itself, otherwise ERR_INVALID_SIZE will be returned.
110 */ 116 */
111 ResultCode Unmap(Process& target_process, VAddr address); 117 ResultCode Unmap(Process& target_process, VAddr address, u64 unmap_size);
112 118
113 /** 119 /**
114 * Gets a pointer to the shared memory block 120 * Gets a pointer to the shared memory block
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 77d0e3d96..2fd07ab34 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -32,6 +32,7 @@
32#include "core/hle/kernel/svc.h" 32#include "core/hle/kernel/svc.h"
33#include "core/hle/kernel/svc_wrap.h" 33#include "core/hle/kernel/svc_wrap.h"
34#include "core/hle/kernel/thread.h" 34#include "core/hle/kernel/thread.h"
35#include "core/hle/kernel/transfer_memory.h"
35#include "core/hle/kernel/writable_event.h" 36#include "core/hle/kernel/writable_event.h"
36#include "core/hle/lock.h" 37#include "core/hle/lock.h"
37#include "core/hle/result.h" 38#include "core/hle/result.h"
@@ -174,11 +175,8 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
174 return ERR_INVALID_SIZE; 175 return ERR_INVALID_SIZE;
175 } 176 }
176 177
177 auto& vm_manager = Core::CurrentProcess()->VMManager(); 178 auto& vm_manager = Core::System::GetInstance().Kernel().CurrentProcess()->VMManager();
178 const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress(); 179 const auto alloc_result = vm_manager.SetHeapSize(heap_size);
179 const auto alloc_result =
180 vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite);
181
182 if (alloc_result.Failed()) { 180 if (alloc_result.Failed()) {
183 return alloc_result.Code(); 181 return alloc_result.Code();
184 } 182 }
@@ -551,9 +549,9 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
551 return ERR_INVALID_ADDRESS; 549 return ERR_INVALID_ADDRESS;
552 } 550 }
553 551
554 auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 552 auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
555 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, 553 return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
556 requesting_thread_handle); 554 requesting_thread_handle);
557} 555}
558 556
559/// Unlock a mutex 557/// Unlock a mutex
@@ -571,7 +569,8 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
571 return ERR_INVALID_ADDRESS; 569 return ERR_INVALID_ADDRESS;
572 } 570 }
573 571
574 return Mutex::Release(mutex_addr); 572 auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
573 return current_process->GetMutex().Release(mutex_addr);
575} 574}
576 575
577enum class BreakType : u32 { 576enum class BreakType : u32 {
@@ -710,7 +709,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
710 HeapRegionBaseAddr = 4, 709 HeapRegionBaseAddr = 4,
711 HeapRegionSize = 5, 710 HeapRegionSize = 5,
712 TotalMemoryUsage = 6, 711 TotalMemoryUsage = 6,
713 TotalHeapUsage = 7, 712 TotalPhysicalMemoryUsed = 7,
714 IsCurrentProcessBeingDebugged = 8, 713 IsCurrentProcessBeingDebugged = 8,
715 RegisterResourceLimit = 9, 714 RegisterResourceLimit = 9,
716 IdleTickCount = 10, 715 IdleTickCount = 10,
@@ -746,7 +745,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
746 case GetInfoType::NewMapRegionBaseAddr: 745 case GetInfoType::NewMapRegionBaseAddr:
747 case GetInfoType::NewMapRegionSize: 746 case GetInfoType::NewMapRegionSize:
748 case GetInfoType::TotalMemoryUsage: 747 case GetInfoType::TotalMemoryUsage:
749 case GetInfoType::TotalHeapUsage: 748 case GetInfoType::TotalPhysicalMemoryUsed:
750 case GetInfoType::IsVirtualAddressMemoryEnabled: 749 case GetInfoType::IsVirtualAddressMemoryEnabled:
751 case GetInfoType::PersonalMmHeapUsage: 750 case GetInfoType::PersonalMmHeapUsage:
752 case GetInfoType::TitleId: 751 case GetInfoType::TitleId:
@@ -806,8 +805,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
806 *result = process->VMManager().GetTotalMemoryUsage(); 805 *result = process->VMManager().GetTotalMemoryUsage();
807 return RESULT_SUCCESS; 806 return RESULT_SUCCESS;
808 807
809 case GetInfoType::TotalHeapUsage: 808 case GetInfoType::TotalPhysicalMemoryUsed:
810 *result = process->VMManager().GetTotalHeapUsage(); 809 *result = process->GetTotalPhysicalMemoryUsed();
811 return RESULT_SUCCESS; 810 return RESULT_SUCCESS;
812 811
813 case GetInfoType::IsVirtualAddressMemoryEnabled: 812 case GetInfoType::IsVirtualAddressMemoryEnabled:
@@ -1141,7 +1140,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
1141 return ERR_INVALID_MEMORY_RANGE; 1140 return ERR_INVALID_MEMORY_RANGE;
1142 } 1141 }
1143 1142
1144 return shared_memory->Unmap(*current_process, addr); 1143 return shared_memory->Unmap(*current_process, addr, size);
1145} 1144}
1146 1145
1147static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address, 1146static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address,
@@ -1284,10 +1283,14 @@ static ResultCode StartThread(Handle thread_handle) {
1284 1283
1285/// Called when a thread exits 1284/// Called when a thread exits
1286static void ExitThread() { 1285static void ExitThread() {
1287 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC()); 1286 auto& system = Core::System::GetInstance();
1288 1287
1289 ExitCurrentThread(); 1288 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
1290 Core::System::GetInstance().PrepareReschedule(); 1289
1290 auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
1291 current_thread->Stop();
1292 system.CurrentScheduler().RemoveThread(current_thread);
1293 system.PrepareReschedule();
1291} 1294}
1292 1295
1293/// Sleep the current thread 1296/// Sleep the current thread
@@ -1300,32 +1303,32 @@ static void SleepThread(s64 nanoseconds) {
1300 YieldAndWaitForLoadBalancing = -2, 1303 YieldAndWaitForLoadBalancing = -2,
1301 }; 1304 };
1302 1305
1306 auto& system = Core::System::GetInstance();
1307 auto& scheduler = system.CurrentScheduler();
1308 auto* const current_thread = scheduler.GetCurrentThread();
1309
1303 if (nanoseconds <= 0) { 1310 if (nanoseconds <= 0) {
1304 auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
1305 switch (static_cast<SleepType>(nanoseconds)) { 1311 switch (static_cast<SleepType>(nanoseconds)) {
1306 case SleepType::YieldWithoutLoadBalancing: 1312 case SleepType::YieldWithoutLoadBalancing:
1307 scheduler.YieldWithoutLoadBalancing(GetCurrentThread()); 1313 scheduler.YieldWithoutLoadBalancing(current_thread);
1308 break; 1314 break;
1309 case SleepType::YieldWithLoadBalancing: 1315 case SleepType::YieldWithLoadBalancing:
1310 scheduler.YieldWithLoadBalancing(GetCurrentThread()); 1316 scheduler.YieldWithLoadBalancing(current_thread);
1311 break; 1317 break;
1312 case SleepType::YieldAndWaitForLoadBalancing: 1318 case SleepType::YieldAndWaitForLoadBalancing:
1313 scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread()); 1319 scheduler.YieldAndWaitForLoadBalancing(current_thread);
1314 break; 1320 break;
1315 default: 1321 default:
1316 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); 1322 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
1317 } 1323 }
1318 } else { 1324 } else {
1319 // Sleep current thread and check for next thread to schedule 1325 current_thread->Sleep(nanoseconds);
1320 WaitCurrentThread_Sleep();
1321
1322 // Create an event to wake the thread up after the specified nanosecond delay has passed
1323 GetCurrentThread()->WakeAfterDelay(nanoseconds);
1324 } 1326 }
1325 1327
1326 // Reschedule all CPU cores 1328 // Reschedule all CPU cores
1327 for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) 1329 for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
1328 Core::System::GetInstance().CpuCore(i).PrepareReschedule(); 1330 system.CpuCore(i).PrepareReschedule();
1331 }
1329} 1332}
1330 1333
1331/// Wait process wide key atomic 1334/// Wait process wide key atomic
@@ -1336,17 +1339,35 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
1336 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", 1339 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
1337 mutex_addr, condition_variable_addr, thread_handle, nano_seconds); 1340 mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
1338 1341
1339 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1342 if (Memory::IsKernelVirtualAddress(mutex_addr)) {
1343 LOG_ERROR(
1344 Kernel_SVC,
1345 "Given mutex address must not be within the kernel address space. address=0x{:016X}",
1346 mutex_addr);
1347 return ERR_INVALID_ADDRESS_STATE;
1348 }
1349
1350 if (!Common::IsWordAligned(mutex_addr)) {
1351 LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}",
1352 mutex_addr);
1353 return ERR_INVALID_ADDRESS;
1354 }
1355
1356 auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
1357 const auto& handle_table = current_process->GetHandleTable();
1340 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); 1358 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1341 ASSERT(thread); 1359 ASSERT(thread);
1342 1360
1343 CASCADE_CODE(Mutex::Release(mutex_addr)); 1361 const auto release_result = current_process->GetMutex().Release(mutex_addr);
1362 if (release_result.IsError()) {
1363 return release_result;
1364 }
1344 1365
1345 SharedPtr<Thread> current_thread = GetCurrentThread(); 1366 SharedPtr<Thread> current_thread = GetCurrentThread();
1346 current_thread->SetCondVarWaitAddress(condition_variable_addr); 1367 current_thread->SetCondVarWaitAddress(condition_variable_addr);
1347 current_thread->SetMutexWaitAddress(mutex_addr); 1368 current_thread->SetMutexWaitAddress(mutex_addr);
1348 current_thread->SetWaitHandle(thread_handle); 1369 current_thread->SetWaitHandle(thread_handle);
1349 current_thread->SetStatus(ThreadStatus::WaitMutex); 1370 current_thread->SetStatus(ThreadStatus::WaitCondVar);
1350 current_thread->InvalidateWakeupCallback(); 1371 current_thread->InvalidateWakeupCallback();
1351 1372
1352 current_thread->WakeAfterDelay(nano_seconds); 1373 current_thread->WakeAfterDelay(nano_seconds);
@@ -1390,10 +1411,10 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
1390 // them all. 1411 // them all.
1391 std::size_t last = waiting_threads.size(); 1412 std::size_t last = waiting_threads.size();
1392 if (target != -1) 1413 if (target != -1)
1393 last = target; 1414 last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
1394 1415
1395 // If there are no threads waiting on this condition variable, just exit 1416 // If there are no threads waiting on this condition variable, just exit
1396 if (last > waiting_threads.size()) 1417 if (last == 0)
1397 return RESULT_SUCCESS; 1418 return RESULT_SUCCESS;
1398 1419
1399 for (std::size_t index = 0; index < last; ++index) { 1420 for (std::size_t index = 0; index < last; ++index) {
@@ -1401,6 +1422,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
1401 1422
1402 ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); 1423 ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
1403 1424
1425 // liberate Cond Var Thread.
1426 thread->SetCondVarWaitAddress(0);
1427
1404 std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); 1428 std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
1405 1429
1406 auto& monitor = Core::System::GetInstance().Monitor(); 1430 auto& monitor = Core::System::GetInstance().Monitor();
@@ -1419,10 +1443,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
1419 } 1443 }
1420 } while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(), 1444 } while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
1421 thread->GetWaitHandle())); 1445 thread->GetWaitHandle()));
1422
1423 if (mutex_val == 0) { 1446 if (mutex_val == 0) {
1424 // We were able to acquire the mutex, resume this thread. 1447 // We were able to acquire the mutex, resume this thread.
1425 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); 1448 ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
1426 thread->ResumeFromWait(); 1449 thread->ResumeFromWait();
1427 1450
1428 auto* const lock_owner = thread->GetLockOwner(); 1451 auto* const lock_owner = thread->GetLockOwner();
@@ -1432,8 +1455,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
1432 1455
1433 thread->SetLockOwner(nullptr); 1456 thread->SetLockOwner(nullptr);
1434 thread->SetMutexWaitAddress(0); 1457 thread->SetMutexWaitAddress(0);
1435 thread->SetCondVarWaitAddress(0);
1436 thread->SetWaitHandle(0); 1458 thread->SetWaitHandle(0);
1459 Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
1437 } else { 1460 } else {
1438 // Atomically signal that the mutex now has a waiting thread. 1461 // Atomically signal that the mutex now has a waiting thread.
1439 do { 1462 do {
@@ -1452,12 +1475,11 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
1452 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1475 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1453 auto owner = handle_table.Get<Thread>(owner_handle); 1476 auto owner = handle_table.Get<Thread>(owner_handle);
1454 ASSERT(owner); 1477 ASSERT(owner);
1455 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); 1478 ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
1456 thread->InvalidateWakeupCallback(); 1479 thread->InvalidateWakeupCallback();
1480 thread->SetStatus(ThreadStatus::WaitMutex);
1457 1481
1458 owner->AddMutexWaiter(thread); 1482 owner->AddMutexWaiter(thread);
1459
1460 Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
1461 } 1483 }
1462 } 1484 }
1463 1485
@@ -1577,14 +1599,121 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
1577 } 1599 }
1578 1600
1579 auto& kernel = Core::System::GetInstance().Kernel(); 1601 auto& kernel = Core::System::GetInstance().Kernel();
1580 auto process = kernel.CurrentProcess(); 1602 auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms);
1581 auto& handle_table = process->GetHandleTable();
1582 const auto shared_mem_handle = SharedMemory::Create(kernel, process, size, perms, perms, addr);
1583 1603
1584 CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); 1604 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
1605 const auto result = handle_table.Create(std::move(transfer_mem_handle));
1606 if (result.Failed()) {
1607 return result.Code();
1608 }
1609
1610 *handle = *result;
1585 return RESULT_SUCCESS; 1611 return RESULT_SUCCESS;
1586} 1612}
1587 1613
1614static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 permission_raw) {
1615 LOG_DEBUG(Kernel_SVC,
1616 "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}",
1617 handle, address, size, permission_raw);
1618
1619 if (!Common::Is4KBAligned(address)) {
1620 LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).",
1621 address);
1622 return ERR_INVALID_ADDRESS;
1623 }
1624
1625 if (size == 0 || !Common::Is4KBAligned(size)) {
1626 LOG_ERROR(Kernel_SVC,
1627 "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).",
1628 size);
1629 return ERR_INVALID_SIZE;
1630 }
1631
1632 if (!IsValidAddressRange(address, size)) {
1633 LOG_ERROR(Kernel_SVC,
1634 "Given address and size overflows the 64-bit range (address=0x{:016X}, "
1635 "size=0x{:016X}).",
1636 address, size);
1637 return ERR_INVALID_ADDRESS_STATE;
1638 }
1639
1640 const auto permissions = static_cast<MemoryPermission>(permission_raw);
1641 if (permissions != MemoryPermission::None && permissions != MemoryPermission::Read &&
1642 permissions != MemoryPermission::ReadWrite) {
1643 LOG_ERROR(Kernel_SVC, "Invalid transfer memory permissions given (permissions=0x{:08X}).",
1644 permission_raw);
1645 return ERR_INVALID_STATE;
1646 }
1647
1648 const auto& kernel = Core::System::GetInstance().Kernel();
1649 const auto* const current_process = kernel.CurrentProcess();
1650 const auto& handle_table = current_process->GetHandleTable();
1651
1652 auto transfer_memory = handle_table.Get<TransferMemory>(handle);
1653 if (!transfer_memory) {
1654 LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).",
1655 handle);
1656 return ERR_INVALID_HANDLE;
1657 }
1658
1659 if (!current_process->VMManager().IsWithinASLRRegion(address, size)) {
1660 LOG_ERROR(Kernel_SVC,
1661 "Given address and size don't fully fit within the ASLR region "
1662 "(address=0x{:016X}, size=0x{:016X}).",
1663 address, size);
1664 return ERR_INVALID_MEMORY_RANGE;
1665 }
1666
1667 return transfer_memory->MapMemory(address, size, permissions);
1668}
1669
1670static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) {
1671 LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle,
1672 address, size);
1673
1674 if (!Common::Is4KBAligned(address)) {
1675 LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).",
1676 address);
1677 return ERR_INVALID_ADDRESS;
1678 }
1679
1680 if (size == 0 || !Common::Is4KBAligned(size)) {
1681 LOG_ERROR(Kernel_SVC,
1682 "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).",
1683 size);
1684 return ERR_INVALID_SIZE;
1685 }
1686
1687 if (!IsValidAddressRange(address, size)) {
1688 LOG_ERROR(Kernel_SVC,
1689 "Given address and size overflows the 64-bit range (address=0x{:016X}, "
1690 "size=0x{:016X}).",
1691 address, size);
1692 return ERR_INVALID_ADDRESS_STATE;
1693 }
1694
1695 const auto& kernel = Core::System::GetInstance().Kernel();
1696 const auto* const current_process = kernel.CurrentProcess();
1697 const auto& handle_table = current_process->GetHandleTable();
1698
1699 auto transfer_memory = handle_table.Get<TransferMemory>(handle);
1700 if (!transfer_memory) {
1701 LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).",
1702 handle);
1703 return ERR_INVALID_HANDLE;
1704 }
1705
1706 if (!current_process->VMManager().IsWithinASLRRegion(address, size)) {
1707 LOG_ERROR(Kernel_SVC,
1708 "Given address and size don't fully fit within the ASLR region "
1709 "(address=0x{:016X}, size=0x{:016X}).",
1710 address, size);
1711 return ERR_INVALID_MEMORY_RANGE;
1712 }
1713
1714 return transfer_memory->UnmapMemory(address, size);
1715}
1716
1588static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { 1717static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
1589 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 1718 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
1590 1719
@@ -1868,6 +1997,83 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource
1868 return RESULT_SUCCESS; 1997 return RESULT_SUCCESS;
1869} 1998}
1870 1999
2000static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids,
2001 u32 out_process_ids_size) {
2002 LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
2003 out_process_ids, out_process_ids_size);
2004
2005 // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
2006 if ((out_process_ids_size & 0xF0000000) != 0) {
2007 LOG_ERROR(Kernel_SVC,
2008 "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
2009 out_process_ids_size);
2010 return ERR_OUT_OF_RANGE;
2011 }
2012
2013 const auto& kernel = Core::System::GetInstance().Kernel();
2014 const auto& vm_manager = kernel.CurrentProcess()->VMManager();
2015 const auto total_copy_size = out_process_ids_size * sizeof(u64);
2016
2017 if (out_process_ids_size > 0 &&
2018 !vm_manager.IsWithinAddressSpace(out_process_ids, total_copy_size)) {
2019 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
2020 out_process_ids, out_process_ids + total_copy_size);
2021 return ERR_INVALID_ADDRESS_STATE;
2022 }
2023
2024 const auto& process_list = kernel.GetProcessList();
2025 const auto num_processes = process_list.size();
2026 const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
2027
2028 for (std::size_t i = 0; i < copy_amount; ++i) {
2029 Memory::Write64(out_process_ids, process_list[i]->GetProcessID());
2030 out_process_ids += sizeof(u64);
2031 }
2032
2033 *out_num_processes = static_cast<u32>(num_processes);
2034 return RESULT_SUCCESS;
2035}
2036
2037ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size,
2038 Handle debug_handle) {
2039 // TODO: Handle this case when debug events are supported.
2040 UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
2041
2042 LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
2043 out_thread_ids, out_thread_ids_size);
2044
2045 // If the size is negative or larger than INT32_MAX / sizeof(u64)
2046 if ((out_thread_ids_size & 0xF0000000) != 0) {
2047 LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
2048 out_thread_ids_size);
2049 return ERR_OUT_OF_RANGE;
2050 }
2051
2052 const auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
2053 const auto& vm_manager = current_process->VMManager();
2054 const auto total_copy_size = out_thread_ids_size * sizeof(u64);
2055
2056 if (out_thread_ids_size > 0 &&
2057 !vm_manager.IsWithinAddressSpace(out_thread_ids, total_copy_size)) {
2058 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
2059 out_thread_ids, out_thread_ids + total_copy_size);
2060 return ERR_INVALID_ADDRESS_STATE;
2061 }
2062
2063 const auto& thread_list = current_process->GetThreadList();
2064 const auto num_threads = thread_list.size();
2065 const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
2066
2067 auto list_iter = thread_list.cbegin();
2068 for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
2069 Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID());
2070 out_thread_ids += sizeof(u64);
2071 }
2072
2073 *out_num_threads = static_cast<u32>(num_threads);
2074 return RESULT_SUCCESS;
2075}
2076
1871namespace { 2077namespace {
1872struct FunctionDef { 2078struct FunctionDef {
1873 using Func = void(); 2079 using Func = void();
@@ -1960,8 +2166,8 @@ static const FunctionDef SVC_Table[] = {
1960 {0x4E, nullptr, "ReadWriteRegister"}, 2166 {0x4E, nullptr, "ReadWriteRegister"},
1961 {0x4F, nullptr, "SetProcessActivity"}, 2167 {0x4F, nullptr, "SetProcessActivity"},
1962 {0x50, SvcWrap<CreateSharedMemory>, "CreateSharedMemory"}, 2168 {0x50, SvcWrap<CreateSharedMemory>, "CreateSharedMemory"},
1963 {0x51, nullptr, "MapTransferMemory"}, 2169 {0x51, SvcWrap<MapTransferMemory>, "MapTransferMemory"},
1964 {0x52, nullptr, "UnmapTransferMemory"}, 2170 {0x52, SvcWrap<UnmapTransferMemory>, "UnmapTransferMemory"},
1965 {0x53, nullptr, "CreateInterruptEvent"}, 2171 {0x53, nullptr, "CreateInterruptEvent"},
1966 {0x54, nullptr, "QueryPhysicalAddress"}, 2172 {0x54, nullptr, "QueryPhysicalAddress"},
1967 {0x55, nullptr, "QueryIoMapping"}, 2173 {0x55, nullptr, "QueryIoMapping"},
@@ -1980,8 +2186,8 @@ static const FunctionDef SVC_Table[] = {
1980 {0x62, nullptr, "TerminateDebugProcess"}, 2186 {0x62, nullptr, "TerminateDebugProcess"},
1981 {0x63, nullptr, "GetDebugEvent"}, 2187 {0x63, nullptr, "GetDebugEvent"},
1982 {0x64, nullptr, "ContinueDebugEvent"}, 2188 {0x64, nullptr, "ContinueDebugEvent"},
1983 {0x65, nullptr, "GetProcessList"}, 2189 {0x65, SvcWrap<GetProcessList>, "GetProcessList"},
1984 {0x66, nullptr, "GetThreadList"}, 2190 {0x66, SvcWrap<GetThreadList>, "GetThreadList"},
1985 {0x67, nullptr, "GetDebugThreadContext"}, 2191 {0x67, nullptr, "GetDebugThreadContext"},
1986 {0x68, nullptr, "SetDebugThreadContext"}, 2192 {0x68, nullptr, "SetDebugThreadContext"},
1987 {0x69, nullptr, "QueryDebugProcessMemory"}, 2193 {0x69, nullptr, "QueryDebugProcessMemory"},
@@ -2023,7 +2229,7 @@ void CallSVC(u32 immediate) {
2023 MICROPROFILE_SCOPE(Kernel_SVC); 2229 MICROPROFILE_SCOPE(Kernel_SVC);
2024 2230
2025 // Lock the global kernel mutex when we enter the kernel HLE. 2231 // Lock the global kernel mutex when we enter the kernel HLE.
2026 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 2232 std::lock_guard lock{HLE::g_hle_lock};
2027 2233
2028 const FunctionDef* info = GetSVCInfo(immediate); 2234 const FunctionDef* info = GetSVCInfo(immediate);
2029 if (info) { 2235 if (info) {
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 2a2c2c5ea..b3733680f 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -78,6 +78,14 @@ void SvcWrap() {
78 FuncReturn(retval); 78 FuncReturn(retval);
79} 79}
80 80
81template <ResultCode func(u32*, u64, u32)>
82void SvcWrap() {
83 u32 param_1 = 0;
84 const u32 retval = func(&param_1, Param(1), static_cast<u32>(Param(2))).raw;
85 Core::CurrentArmInterface().SetReg(1, param_1);
86 FuncReturn(retval);
87}
88
81template <ResultCode func(u64*, u32)> 89template <ResultCode func(u64*, u32)>
82void SvcWrap() { 90void SvcWrap() {
83 u64 param_1 = 0; 91 u64 param_1 = 0;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index eb54d6651..1b891f632 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -7,8 +7,6 @@
7#include <optional> 7#include <optional>
8#include <vector> 8#include <vector>
9 9
10#include <boost/range/algorithm_ext/erase.hpp>
11
12#include "common/assert.h" 10#include "common/assert.h"
13#include "common/common_types.h" 11#include "common/common_types.h"
14#include "common/logging/log.h" 12#include "common/logging/log.h"
@@ -30,7 +28,7 @@
30 28
31namespace Kernel { 29namespace Kernel {
32 30
33bool Thread::ShouldWait(Thread* thread) const { 31bool Thread::ShouldWait(const Thread* thread) const {
34 return status != ThreadStatus::Dead; 32 return status != ThreadStatus::Dead;
35} 33}
36 34
@@ -64,21 +62,12 @@ void Thread::Stop() {
64 } 62 }
65 wait_objects.clear(); 63 wait_objects.clear();
66 64
65 owner_process->UnregisterThread(this);
66
67 // Mark the TLS slot in the thread's page as free. 67 // Mark the TLS slot in the thread's page as free.
68 owner_process->FreeTLSSlot(tls_address); 68 owner_process->FreeTLSSlot(tls_address);
69} 69}
70 70
71void WaitCurrentThread_Sleep() {
72 Thread* thread = GetCurrentThread();
73 thread->SetStatus(ThreadStatus::WaitSleep);
74}
75
76void ExitCurrentThread() {
77 Thread* thread = GetCurrentThread();
78 thread->Stop();
79 Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
80}
81
82void Thread::WakeAfterDelay(s64 nanoseconds) { 71void Thread::WakeAfterDelay(s64 nanoseconds) {
83 // Don't schedule a wakeup if the thread wants to wait forever 72 // Don't schedule a wakeup if the thread wants to wait forever
84 if (nanoseconds == -1) 73 if (nanoseconds == -1)
@@ -118,6 +107,7 @@ void Thread::ResumeFromWait() {
118 case ThreadStatus::WaitSleep: 107 case ThreadStatus::WaitSleep:
119 case ThreadStatus::WaitIPC: 108 case ThreadStatus::WaitIPC:
120 case ThreadStatus::WaitMutex: 109 case ThreadStatus::WaitMutex:
110 case ThreadStatus::WaitCondVar:
121 case ThreadStatus::WaitArb: 111 case ThreadStatus::WaitArb:
122 break; 112 break;
123 113
@@ -211,9 +201,11 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
211 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 201 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
212 thread->owner_process = &owner_process; 202 thread->owner_process = &owner_process;
213 thread->scheduler = &system.Scheduler(processor_id); 203 thread->scheduler = &system.Scheduler(processor_id);
214 thread->scheduler->AddThread(thread, priority); 204 thread->scheduler->AddThread(thread);
215 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); 205 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
216 206
207 thread->owner_process->RegisterThread(thread.get());
208
217 // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used 209 // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
218 // to initialize the context 210 // to initialize the context
219 ResetThreadContext(thread->context, stack_top, entry_point, arg); 211 ResetThreadContext(thread->context, stack_top, entry_point, arg);
@@ -241,16 +233,16 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
241 context.cpu_registers[1] = output; 233 context.cpu_registers[1] = output;
242} 234}
243 235
244s32 Thread::GetWaitObjectIndex(WaitObject* object) const { 236s32 Thread::GetWaitObjectIndex(const WaitObject* object) const {
245 ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); 237 ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything");
246 auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); 238 const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
247 return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1); 239 return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1);
248} 240}
249 241
250VAddr Thread::GetCommandBufferAddress() const { 242VAddr Thread::GetCommandBufferAddress() const {
251 // Offset from the start of TLS at which the IPC command buffer begins. 243 // Offset from the start of TLS at which the IPC command buffer begins.
252 static constexpr int CommandHeaderOffset = 0x80; 244 constexpr u64 command_header_offset = 0x80;
253 return GetTLSAddress() + CommandHeaderOffset; 245 return GetTLSAddress() + command_header_offset;
254} 246}
255 247
256void Thread::SetStatus(ThreadStatus new_status) { 248void Thread::SetStatus(ThreadStatus new_status) {
@@ -269,8 +261,8 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
269 if (thread->lock_owner == this) { 261 if (thread->lock_owner == this) {
270 // If the thread is already waiting for this thread to release the mutex, ensure that the 262 // If the thread is already waiting for this thread to release the mutex, ensure that the
271 // waiters list is consistent and return without doing anything. 263 // waiters list is consistent and return without doing anything.
272 auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); 264 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
273 ASSERT(itr != wait_mutex_threads.end()); 265 ASSERT(iter != wait_mutex_threads.end());
274 return; 266 return;
275 } 267 }
276 268
@@ -278,11 +270,16 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
278 ASSERT(thread->lock_owner == nullptr); 270 ASSERT(thread->lock_owner == nullptr);
279 271
280 // Ensure that the thread is not already in the list of mutex waiters 272 // Ensure that the thread is not already in the list of mutex waiters
281 auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); 273 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
282 ASSERT(itr == wait_mutex_threads.end()); 274 ASSERT(iter == wait_mutex_threads.end());
283 275
276 // Keep the list in an ordered fashion
277 const auto insertion_point = std::find_if(
278 wait_mutex_threads.begin(), wait_mutex_threads.end(),
279 [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
280 wait_mutex_threads.insert(insertion_point, thread);
284 thread->lock_owner = this; 281 thread->lock_owner = this;
285 wait_mutex_threads.emplace_back(std::move(thread)); 282
286 UpdatePriority(); 283 UpdatePriority();
287} 284}
288 285
@@ -290,32 +287,44 @@ void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
290 ASSERT(thread->lock_owner == this); 287 ASSERT(thread->lock_owner == this);
291 288
292 // Ensure that the thread is in the list of mutex waiters 289 // Ensure that the thread is in the list of mutex waiters
293 auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); 290 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
294 ASSERT(itr != wait_mutex_threads.end()); 291 ASSERT(iter != wait_mutex_threads.end());
292
293 wait_mutex_threads.erase(iter);
295 294
296 boost::remove_erase(wait_mutex_threads, thread);
297 thread->lock_owner = nullptr; 295 thread->lock_owner = nullptr;
298 UpdatePriority(); 296 UpdatePriority();
299} 297}
300 298
301void Thread::UpdatePriority() { 299void Thread::UpdatePriority() {
302 // Find the highest priority among all the threads that are waiting for this thread's lock 300 // If any of the threads waiting on the mutex have a higher priority
301 // (taking into account priority inheritance), then this thread inherits
302 // that thread's priority.
303 u32 new_priority = nominal_priority; 303 u32 new_priority = nominal_priority;
304 for (const auto& thread : wait_mutex_threads) { 304 if (!wait_mutex_threads.empty()) {
305 if (thread->nominal_priority < new_priority) 305 if (wait_mutex_threads.front()->current_priority < new_priority) {
306 new_priority = thread->nominal_priority; 306 new_priority = wait_mutex_threads.front()->current_priority;
307 }
307 } 308 }
308 309
309 if (new_priority == current_priority) 310 if (new_priority == current_priority) {
310 return; 311 return;
312 }
311 313
312 scheduler->SetThreadPriority(this, new_priority); 314 scheduler->SetThreadPriority(this, new_priority);
313
314 current_priority = new_priority; 315 current_priority = new_priority;
315 316
317 if (!lock_owner) {
318 return;
319 }
320
321 // Ensure that the thread is within the correct location in the waiting list.
322 auto old_owner = lock_owner;
323 lock_owner->RemoveMutexWaiter(this);
324 old_owner->AddMutexWaiter(this);
325
316 // Recursively update the priority of the thread that depends on the priority of this one. 326 // Recursively update the priority of the thread that depends on the priority of this one.
317 if (lock_owner) 327 lock_owner->UpdatePriority();
318 lock_owner->UpdatePriority();
319} 328}
320 329
321void Thread::ChangeCore(u32 core, u64 mask) { 330void Thread::ChangeCore(u32 core, u64 mask) {
@@ -347,7 +356,7 @@ void Thread::ChangeScheduler() {
347 if (*new_processor_id != processor_id) { 356 if (*new_processor_id != processor_id) {
348 // Remove thread from previous core's scheduler 357 // Remove thread from previous core's scheduler
349 scheduler->RemoveThread(this); 358 scheduler->RemoveThread(this);
350 next_scheduler.AddThread(this, current_priority); 359 next_scheduler.AddThread(this);
351 } 360 }
352 361
353 processor_id = *new_processor_id; 362 processor_id = *new_processor_id;
@@ -362,7 +371,7 @@ void Thread::ChangeScheduler() {
362 system.CpuCore(processor_id).PrepareReschedule(); 371 system.CpuCore(processor_id).PrepareReschedule();
363} 372}
364 373
365bool Thread::AllWaitObjectsReady() { 374bool Thread::AllWaitObjectsReady() const {
366 return std::none_of( 375 return std::none_of(
367 wait_objects.begin(), wait_objects.end(), 376 wait_objects.begin(), wait_objects.end(),
368 [this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); }); 377 [this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); });
@@ -391,6 +400,14 @@ void Thread::SetActivity(ThreadActivity value) {
391 } 400 }
392} 401}
393 402
403void Thread::Sleep(s64 nanoseconds) {
404 // Sleep current thread and check for next thread to schedule
405 SetStatus(ThreadStatus::WaitSleep);
406
407 // Create an event to wake the thread up after the specified nanosecond delay has passed
408 WakeAfterDelay(nanoseconds);
409}
410
394//////////////////////////////////////////////////////////////////////////////////////////////////// 411////////////////////////////////////////////////////////////////////////////////////////////////////
395 412
396/** 413/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index c48b21aba..73e5d1bb4 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -51,7 +51,8 @@ enum class ThreadStatus {
51 WaitIPC, ///< Waiting for the reply from an IPC request 51 WaitIPC, ///< Waiting for the reply from an IPC request
52 WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false 52 WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
53 WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true 53 WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
54 WaitMutex, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc 54 WaitMutex, ///< Waiting due to an ArbitrateLock svc
55 WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc
55 WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc 56 WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
56 Dormant, ///< Created but not yet made ready 57 Dormant, ///< Created but not yet made ready
57 Dead ///< Run to completion, or forcefully terminated 58 Dead ///< Run to completion, or forcefully terminated
@@ -110,7 +111,7 @@ public:
110 return HANDLE_TYPE; 111 return HANDLE_TYPE;
111 } 112 }
112 113
113 bool ShouldWait(Thread* thread) const override; 114 bool ShouldWait(const Thread* thread) const override;
114 void Acquire(Thread* thread) override; 115 void Acquire(Thread* thread) override;
115 116
116 /** 117 /**
@@ -204,7 +205,7 @@ public:
204 * object in the list. 205 * object in the list.
205 * @param object Object to query the index of. 206 * @param object Object to query the index of.
206 */ 207 */
207 s32 GetWaitObjectIndex(WaitObject* object) const; 208 s32 GetWaitObjectIndex(const WaitObject* object) const;
208 209
209 /** 210 /**
210 * Stops a thread, invalidating it from further use 211 * Stops a thread, invalidating it from further use
@@ -298,7 +299,7 @@ public:
298 } 299 }
299 300
300 /// Determines whether all the objects this thread is waiting on are ready. 301 /// Determines whether all the objects this thread is waiting on are ready.
301 bool AllWaitObjectsReady(); 302 bool AllWaitObjectsReady() const;
302 303
303 const MutexWaitingThreads& GetMutexWaitingThreads() const { 304 const MutexWaitingThreads& GetMutexWaitingThreads() const {
304 return wait_mutex_threads; 305 return wait_mutex_threads;
@@ -383,6 +384,9 @@ public:
383 384
384 void SetActivity(ThreadActivity value); 385 void SetActivity(ThreadActivity value);
385 386
387 /// Sleeps this thread for the given amount of nanoseconds.
388 void Sleep(s64 nanoseconds);
389
386private: 390private:
387 explicit Thread(KernelCore& kernel); 391 explicit Thread(KernelCore& kernel);
388 ~Thread() override; 392 ~Thread() override;
@@ -398,8 +402,14 @@ private:
398 VAddr entry_point = 0; 402 VAddr entry_point = 0;
399 VAddr stack_top = 0; 403 VAddr stack_top = 0;
400 404
401 u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application 405 /// Nominal thread priority, as set by the emulated application.
402 u32 current_priority = 0; ///< Current thread priority, can be temporarily changed 406 /// The nominal priority is the thread priority without priority
407 /// inheritance taken into account.
408 u32 nominal_priority = 0;
409
410 /// Current thread priority. This may change over the course of the
411 /// thread's lifetime in order to facilitate priority inheritance.
412 u32 current_priority = 0;
403 413
404 u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. 414 u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
405 u64 last_running_ticks = 0; ///< CPU tick when thread was last running 415 u64 last_running_ticks = 0; ///< CPU tick when thread was last running
@@ -460,14 +470,4 @@ private:
460 */ 470 */
461Thread* GetCurrentThread(); 471Thread* GetCurrentThread();
462 472
463/**
464 * Waits the current thread on a sleep
465 */
466void WaitCurrentThread_Sleep();
467
468/**
469 * Stops the current thread and removes it from the thread_list
470 */
471void ExitCurrentThread();
472
473} // namespace Kernel 473} // namespace Kernel
diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp
new file mode 100644
index 000000000..26c4e5e67
--- /dev/null
+++ b/src/core/hle/kernel/transfer_memory.cpp
@@ -0,0 +1,81 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/kernel/errors.h"
6#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/process.h"
8#include "core/hle/kernel/shared_memory.h"
9#include "core/hle/kernel/transfer_memory.h"
10#include "core/hle/result.h"
11
12namespace Kernel {
13
14TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {}
15TransferMemory::~TransferMemory() = default;
16
17SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, u64 size,
18 MemoryPermission permissions) {
19 SharedPtr<TransferMemory> transfer_memory{new TransferMemory(kernel)};
20
21 transfer_memory->base_address = base_address;
22 transfer_memory->memory_size = size;
23 transfer_memory->owner_permissions = permissions;
24 transfer_memory->owner_process = kernel.CurrentProcess();
25
26 return transfer_memory;
27}
28
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) {
38 if (memory_size != size) {
39 return ERR_INVALID_SIZE;
40 }
41
42 if (owner_permissions != permissions) {
43 return ERR_INVALID_STATE;
44 }
45
46 if (is_mapped) {
47 return ERR_INVALID_STATE;
48 }
49
50 backing_block = std::make_shared<std::vector<u8>>(size);
51
52 const auto map_state = owner_permissions == MemoryPermission::None
53 ? MemoryState::TransferMemoryIsolated
54 : MemoryState::TransferMemory;
55 auto& vm_manager = owner_process->VMManager();
56 const auto map_result = vm_manager.MapMemoryBlock(address, backing_block, 0, size, map_state);
57 if (map_result.Failed()) {
58 return map_result.Code();
59 }
60
61 is_mapped = true;
62 return RESULT_SUCCESS;
63}
64
65ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {
66 if (memory_size != size) {
67 return ERR_INVALID_SIZE;
68 }
69
70 auto& vm_manager = owner_process->VMManager();
71 const auto result = vm_manager.UnmapRange(address, size);
72
73 if (result.IsError()) {
74 return result;
75 }
76
77 is_mapped = false;
78 return RESULT_SUCCESS;
79}
80
81} // namespace Kernel
diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h
new file mode 100644
index 000000000..a140b1e2b
--- /dev/null
+++ b/src/core/hle/kernel/transfer_memory.h
@@ -0,0 +1,103 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <vector>
9
10#include "core/hle/kernel/object.h"
11
12union ResultCode;
13
14namespace Kernel {
15
16class KernelCore;
17class Process;
18
19enum class MemoryPermission : u32;
20
21/// Defines the interface for transfer memory objects.
22///
23/// Transfer memory is typically used for the purpose of
24/// transferring memory between separate process instances,
25/// thus the name.
26///
27class TransferMemory final : public Object {
28public:
29 static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
30
31 static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size,
32 MemoryPermission permissions);
33
34 TransferMemory(const TransferMemory&) = delete;
35 TransferMemory& operator=(const TransferMemory&) = delete;
36
37 TransferMemory(TransferMemory&&) = delete;
38 TransferMemory& operator=(TransferMemory&&) = delete;
39
40 std::string GetTypeName() const override {
41 return "TransferMemory";
42 }
43
44 std::string GetName() const override {
45 return GetTypeName();
46 }
47
48 HandleType GetHandleType() const override {
49 return HANDLE_TYPE;
50 }
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
58 /// Attempts to map transfer memory with the given range and memory permissions.
59 ///
60 /// @param address The base address to being mapping memory at.
61 /// @param size The size of the memory to map, in bytes.
62 /// @param permissions The memory permissions to check against when mapping memory.
63 ///
64 /// @pre The given address, size, and memory permissions must all match
65 /// the same values that were given when creating the transfer memory
66 /// instance.
67 ///
68 ResultCode MapMemory(VAddr address, u64 size, MemoryPermission permissions);
69
70 /// Unmaps the transfer memory with the given range
71 ///
72 /// @param address The base address to begin unmapping memory at.
73 /// @param size The size of the memory to unmap, in bytes.
74 ///
75 /// @pre The given address and size must be the same as the ones used
76 /// to create the transfer memory instance.
77 ///
78 ResultCode UnmapMemory(VAddr address, u64 size);
79
80private:
81 explicit TransferMemory(KernelCore& kernel);
82 ~TransferMemory() override;
83
84 /// Memory block backing this instance.
85 std::shared_ptr<std::vector<u8>> backing_block;
86
87 /// The base address for the memory managed by this instance.
88 VAddr base_address = 0;
89
90 /// Size of the memory, in bytes, that this instance manages.
91 u64 memory_size = 0;
92
93 /// The memory permissions that are applied to this instance.
94 MemoryPermission owner_permissions{};
95
96 /// The process that this transfer memory instance was created under.
97 Process* owner_process = nullptr;
98
99 /// Whether or not this transfer memory instance has mapped memory.
100 bool is_mapped = false;
101};
102
103} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 05c59af34..ec0a480ce 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -7,29 +7,29 @@
7#include <utility> 7#include <utility>
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/memory_hook.h"
10#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "core/file_sys/program_metadata.h" 13#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/errors.h" 14#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/vm_manager.h" 15#include "core/hle/kernel/vm_manager.h"
15#include "core/memory.h" 16#include "core/memory.h"
16#include "core/memory_hook.h"
17#include "core/memory_setup.h" 17#include "core/memory_setup.h"
18 18
19namespace Kernel { 19namespace Kernel {
20namespace { 20namespace {
21const char* GetMemoryStateName(MemoryState state) { 21const char* GetMemoryStateName(MemoryState state) {
22 static constexpr const char* names[] = { 22 static constexpr const char* names[] = {
23 "Unmapped", "Io", 23 "Unmapped", "Io",
24 "Normal", "CodeStatic", 24 "Normal", "Code",
25 "CodeMutable", "Heap", 25 "CodeData", "Heap",
26 "Shared", "Unknown1", 26 "Shared", "Unknown1",
27 "ModuleCodeStatic", "ModuleCodeMutable", 27 "ModuleCode", "ModuleCodeData",
28 "IpcBuffer0", "Stack", 28 "IpcBuffer0", "Stack",
29 "ThreadLocal", "TransferMemoryIsolated", 29 "ThreadLocal", "TransferMemoryIsolated",
30 "TransferMemory", "ProcessMemory", 30 "TransferMemory", "ProcessMemory",
31 "Inaccessible", "IpcBuffer1", 31 "Inaccessible", "IpcBuffer1",
32 "IpcBuffer3", "KernelStack", 32 "IpcBuffer3", "KernelStack",
33 }; 33 };
34 34
35 return names[ToSvcMemoryState(state)]; 35 return names[ToSvcMemoryState(state)];
@@ -177,7 +177,7 @@ ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
177 177
178ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, 178ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
179 MemoryState state, 179 MemoryState state,
180 Memory::MemoryHookPointer mmio_handler) { 180 Common::MemoryHookPointer mmio_handler) {
181 // This is the appropriately sized VMA that will turn into our allocation. 181 // This is the appropriately sized VMA that will turn into our allocation.
182 CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); 182 CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
183 VirtualMemoryArea& final_vma = vma_handle->second; 183 VirtualMemoryArea& final_vma = vma_handle->second;
@@ -256,57 +256,50 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
256 return RESULT_SUCCESS; 256 return RESULT_SUCCESS;
257} 257}
258 258
259ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 259ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
260 if (!IsWithinHeapRegion(target, size)) { 260 if (size > GetHeapRegionSize()) {
261 return ERR_INVALID_ADDRESS; 261 return ERR_OUT_OF_MEMORY;
262 }
263
264 // No need to do any additional work if the heap is already the given size.
265 if (size == GetCurrentHeapSize()) {
266 return MakeResult(heap_region_base);
262 } 267 }
263 268
264 if (heap_memory == nullptr) { 269 if (heap_memory == nullptr) {
265 // Initialize heap 270 // Initialize heap
266 heap_memory = std::make_shared<std::vector<u8>>(); 271 heap_memory = std::make_shared<std::vector<u8>>(size);
267 heap_start = heap_end = target; 272 heap_end = heap_region_base + size;
268 } else { 273 } else {
269 UnmapRange(heap_start, heap_end - heap_start); 274 UnmapRange(heap_region_base, GetCurrentHeapSize());
270 }
271
272 // If necessary, expand backing vector to cover new heap extents.
273 if (target < heap_start) {
274 heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
275 heap_start = target;
276 RefreshMemoryBlockMappings(heap_memory.get());
277 }
278 if (target + size > heap_end) {
279 heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
280 heap_end = target + size;
281 RefreshMemoryBlockMappings(heap_memory.get());
282 } 275 }
283 ASSERT(heap_end - heap_start == heap_memory->size());
284 276
285 CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size, 277 // If necessary, expand backing vector to cover new heap extents in
286 MemoryState::Heap)); 278 // the case of allocating. Otherwise, shrink the backing memory,
287 Reprotect(vma, perms); 279 // if a smaller heap has been requested.
280 const u64 old_heap_size = GetCurrentHeapSize();
281 if (size > old_heap_size) {
282 const u64 alloc_size = size - old_heap_size;
288 283
289 heap_used = size; 284 heap_memory->insert(heap_memory->end(), alloc_size, 0);
290 285 RefreshMemoryBlockMappings(heap_memory.get());
291 return MakeResult<VAddr>(heap_end - size); 286 } else if (size < old_heap_size) {
292} 287 heap_memory->resize(size);
288 heap_memory->shrink_to_fit();
293 289
294ResultCode VMManager::HeapFree(VAddr target, u64 size) { 290 RefreshMemoryBlockMappings(heap_memory.get());
295 if (!IsWithinHeapRegion(target, size)) {
296 return ERR_INVALID_ADDRESS;
297 } 291 }
298 292
299 if (size == 0) { 293 heap_end = heap_region_base + size;
300 return RESULT_SUCCESS; 294 ASSERT(GetCurrentHeapSize() == heap_memory->size());
301 }
302 295
303 const ResultCode result = UnmapRange(target, size); 296 const auto mapping_result =
304 if (result.IsError()) { 297 MapMemoryBlock(heap_region_base, heap_memory, 0, size, MemoryState::Heap);
305 return result; 298 if (mapping_result.Failed()) {
299 return mapping_result.Code();
306 } 300 }
307 301
308 heap_used -= size; 302 return MakeResult<VAddr>(heap_region_base);
309 return RESULT_SUCCESS;
310} 303}
311 304
312MemoryInfo VMManager::QueryMemory(VAddr address) const { 305MemoryInfo VMManager::QueryMemory(VAddr address) const {
@@ -598,6 +591,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
598 591
599 heap_region_base = map_region_end; 592 heap_region_base = map_region_end;
600 heap_region_end = heap_region_base + heap_region_size; 593 heap_region_end = heap_region_base + heap_region_size;
594 heap_end = heap_region_base;
601 595
602 new_map_region_base = heap_region_end; 596 new_map_region_base = heap_region_end;
603 new_map_region_end = new_map_region_base + new_map_region_size; 597 new_map_region_end = new_map_region_base + new_map_region_size;
@@ -624,7 +618,7 @@ void VMManager::ClearPageTable() {
624 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); 618 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
625 page_table.special_regions.clear(); 619 page_table.special_regions.clear();
626 std::fill(page_table.attributes.begin(), page_table.attributes.end(), 620 std::fill(page_table.attributes.begin(), page_table.attributes.end(),
627 Memory::PageType::Unmapped); 621 Common::PageType::Unmapped);
628} 622}
629 623
630VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask, 624VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
@@ -692,10 +686,6 @@ u64 VMManager::GetTotalMemoryUsage() const {
692 return 0xF8000000; 686 return 0xF8000000;
693} 687}
694 688
695u64 VMManager::GetTotalHeapUsage() const {
696 return heap_used;
697}
698
699VAddr VMManager::GetAddressSpaceBaseAddress() const { 689VAddr VMManager::GetAddressSpaceBaseAddress() const {
700 return address_space_base; 690 return address_space_base;
701} 691}
@@ -778,6 +768,10 @@ u64 VMManager::GetHeapRegionSize() const {
778 return heap_region_end - heap_region_base; 768 return heap_region_end - heap_region_base;
779} 769}
780 770
771u64 VMManager::GetCurrentHeapSize() const {
772 return heap_end - heap_region_base;
773}
774
781bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const { 775bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const {
782 return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(), 776 return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(),
783 GetHeapRegionEndAddress()); 777 GetHeapRegionEndAddress());
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 88e0b3c02..6f484b7bf 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -9,9 +9,10 @@
9#include <tuple> 9#include <tuple>
10#include <vector> 10#include <vector>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/memory_hook.h"
13#include "common/page_table.h"
12#include "core/hle/result.h" 14#include "core/hle/result.h"
13#include "core/memory.h" 15#include "core/memory.h"
14#include "core/memory_hook.h"
15 16
16namespace FileSys { 17namespace FileSys {
17enum class ProgramAddressSpaceType : u8; 18enum class ProgramAddressSpaceType : u8;
@@ -164,12 +165,12 @@ enum class MemoryState : u32 {
164 Unmapped = 0x00, 165 Unmapped = 0x00,
165 Io = 0x01 | FlagMapped, 166 Io = 0x01 | FlagMapped,
166 Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed, 167 Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed,
167 CodeStatic = 0x03 | CodeFlags | FlagMapProcess, 168 Code = 0x03 | CodeFlags | FlagMapProcess,
168 CodeMutable = 0x04 | CodeFlags | FlagMapProcess | FlagCodeMemory, 169 CodeData = 0x04 | DataFlags | FlagMapProcess | FlagCodeMemory,
169 Heap = 0x05 | DataFlags | FlagCodeMemory, 170 Heap = 0x05 | DataFlags | FlagCodeMemory,
170 Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated, 171 Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated,
171 ModuleCodeStatic = 0x08 | CodeFlags | FlagModule | FlagMapProcess, 172 ModuleCode = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
172 ModuleCodeMutable = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory, 173 ModuleCodeData = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
173 174
174 IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated | 175 IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated |
175 IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned, 176 IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned,
@@ -290,7 +291,7 @@ struct VirtualMemoryArea {
290 // Settings for type = MMIO 291 // Settings for type = MMIO
291 /// Physical address of the register area this VMA maps to. 292 /// Physical address of the register area this VMA maps to.
292 PAddr paddr = 0; 293 PAddr paddr = 0;
293 Memory::MemoryHookPointer mmio_handler = nullptr; 294 Common::MemoryHookPointer mmio_handler = nullptr;
294 295
295 /// Tests if this area can be merged to the right with `next`. 296 /// Tests if this area can be merged to the right with `next`.
296 bool CanBeMergedWith(const VirtualMemoryArea& next) const; 297 bool CanBeMergedWith(const VirtualMemoryArea& next) const;
@@ -368,7 +369,7 @@ public:
368 * @param mmio_handler The handler that will implement read and write for this MMIO region. 369 * @param mmio_handler The handler that will implement read and write for this MMIO region.
369 */ 370 */
370 ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, 371 ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state,
371 Memory::MemoryHookPointer mmio_handler); 372 Common::MemoryHookPointer mmio_handler);
372 373
373 /// Unmaps a range of addresses, splitting VMAs as necessary. 374 /// Unmaps a range of addresses, splitting VMAs as necessary.
374 ResultCode UnmapRange(VAddr target, u64 size); 375 ResultCode UnmapRange(VAddr target, u64 size);
@@ -379,11 +380,41 @@ public:
379 /// Changes the permissions of a range of addresses, splitting VMAs as necessary. 380 /// Changes the permissions of a range of addresses, splitting VMAs as necessary.
380 ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); 381 ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
381 382
382 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
383 ResultCode HeapFree(VAddr target, u64 size);
384
385 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state); 383 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
386 384
385 /// Attempts to allocate a heap with the given size.
386 ///
387 /// @param size The size of the heap to allocate in bytes.
388 ///
389 /// @note If a heap is currently allocated, and this is called
390 /// with a size that is equal to the size of the current heap,
391 /// then this function will do nothing and return the current
392 /// heap's starting address, as there's no need to perform
393 /// any additional heap allocation work.
394 ///
395 /// @note If a heap is currently allocated, and this is called
396 /// with a size less than the current heap's size, then
397 /// this function will attempt to shrink the heap.
398 ///
399 /// @note If a heap is currently allocated, and this is called
400 /// with a size larger than the current heap's size, then
401 /// this function will attempt to extend the size of the heap.
402 ///
403 /// @returns A result indicating either success or failure.
404 /// <p>
405 /// If successful, this function will return a result
406 /// containing the starting address to the allocated heap.
407 /// <p>
408 /// If unsuccessful, this function will return a result
409 /// containing an error code.
410 ///
411 /// @pre The given size must lie within the allowable heap
412 /// memory region managed by this VMManager instance.
413 /// Failure to abide by this will result in ERR_OUT_OF_MEMORY
414 /// being returned as the result.
415 ///
416 ResultVal<VAddr> SetHeapSize(u64 size);
417
387 /// Queries the memory manager for information about the given address. 418 /// Queries the memory manager for information about the given address.
388 /// 419 ///
389 /// @param address The address to query the memory manager about for information. 420 /// @param address The address to query the memory manager about for information.
@@ -417,9 +448,6 @@ public:
417 /// Gets the total memory usage, used by svcGetInfo 448 /// Gets the total memory usage, used by svcGetInfo
418 u64 GetTotalMemoryUsage() const; 449 u64 GetTotalMemoryUsage() const;
419 450
420 /// Gets the total heap usage, used by svcGetInfo
421 u64 GetTotalHeapUsage() const;
422
423 /// Gets the address space base address 451 /// Gets the address space base address
424 VAddr GetAddressSpaceBaseAddress() const; 452 VAddr GetAddressSpaceBaseAddress() const;
425 453
@@ -468,6 +496,13 @@ public:
468 /// Gets the total size of the heap region in bytes. 496 /// Gets the total size of the heap region in bytes.
469 u64 GetHeapRegionSize() const; 497 u64 GetHeapRegionSize() const;
470 498
499 /// Gets the total size of the current heap in bytes.
500 ///
501 /// @note This is the current allocated heap size, not the size
502 /// of the region it's allowed to exist within.
503 ///
504 u64 GetCurrentHeapSize() const;
505
471 /// Determines whether or not the specified range is within the heap region. 506 /// Determines whether or not the specified range is within the heap region.
472 bool IsWithinHeapRegion(VAddr address, u64 size) const; 507 bool IsWithinHeapRegion(VAddr address, u64 size) const;
473 508
@@ -509,7 +544,7 @@ public:
509 544
510 /// Each VMManager has its own page table, which is set as the main one when the owning process 545 /// Each VMManager has its own page table, which is set as the main one when the owning process
511 /// is scheduled. 546 /// is scheduled.
512 Memory::PageTable page_table; 547 Common::PageTable page_table{Memory::PAGE_BITS};
513 548
514private: 549private:
515 using VMAIter = VMAMap::iterator; 550 using VMAIter = VMAMap::iterator;
@@ -624,9 +659,9 @@ private:
624 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous 659 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
625 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. 660 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
626 std::shared_ptr<std::vector<u8>> heap_memory; 661 std::shared_ptr<std::vector<u8>> heap_memory;
627 // The left/right bounds of the address space covered by heap_memory. 662
628 VAddr heap_start = 0; 663 // The end of the currently allocated heap. This is not an inclusive
664 // end of the range. This is essentially 'base_address + current_size'.
629 VAddr heap_end = 0; 665 VAddr heap_end = 0;
630 u64 heap_used = 0;
631}; 666};
632} // namespace Kernel 667} // namespace Kernel
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h
index 5987fb971..04464a51a 100644
--- a/src/core/hle/kernel/wait_object.h
+++ b/src/core/hle/kernel/wait_object.h
@@ -24,7 +24,7 @@ public:
24 * @param thread The thread about which we're deciding. 24 * @param thread The thread about which we're deciding.
25 * @return True if the current thread should wait due to this object being unavailable 25 * @return True if the current thread should wait due to this object being unavailable
26 */ 26 */
27 virtual bool ShouldWait(Thread* thread) const = 0; 27 virtual bool ShouldWait(const Thread* thread) const = 0;
28 28
29 /// Acquire/lock the object for the specified thread if it is available 29 /// Acquire/lock the object for the specified thread if it is available
30 virtual void Acquire(Thread* thread) = 0; 30 virtual void Acquire(Thread* thread) = 0;
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/am/am.cpp b/src/core/hle/service/am/am.cpp
index 3f009d2b7..85271d418 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -2,10 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <array> 6#include <array>
6#include <cinttypes> 7#include <cinttypes>
7#include <cstring> 8#include <cstring>
8#include <stack>
9#include "audio_core/audio_renderer.h" 9#include "audio_core/audio_renderer.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/file_sys/savedata_factory.h" 11#include "core/file_sys/savedata_factory.h"
@@ -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"
@@ -93,38 +93,84 @@ void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx)
93} 93}
94 94
95IAudioController::IAudioController() : ServiceFramework("IAudioController") { 95IAudioController::IAudioController() : ServiceFramework("IAudioController") {
96 // clang-format off
96 static const FunctionInfo functions[] = { 97 static const FunctionInfo functions[] = {
97 {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"}, 98 {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
98 {1, &IAudioController::GetMainAppletExpectedMasterVolume, 99 {1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"},
99 "GetMainAppletExpectedMasterVolume"}, 100 {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"},
100 {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, 101 {3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"},
101 "GetLibraryAppletExpectedMasterVolume"}, 102 {4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"},
102 {3, nullptr, "ChangeMainAppletMasterVolume"},
103 {4, nullptr, "SetTransparentVolumeRate"},
104 }; 103 };
104 // clang-format on
105
105 RegisterHandlers(functions); 106 RegisterHandlers(functions);
106} 107}
107 108
108IAudioController::~IAudioController() = default; 109IAudioController::~IAudioController() = default;
109 110
110void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) { 111void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
111 LOG_WARNING(Service_AM, "(STUBBED) called"); 112 IPC::RequestParser rp{ctx};
113 const float main_applet_volume_tmp = rp.Pop<float>();
114 const float library_applet_volume_tmp = rp.Pop<float>();
115
116 LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
117 main_applet_volume_tmp, library_applet_volume_tmp);
118
119 // Ensure the volume values remain within the 0-100% range
120 main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
121 library_applet_volume =
122 std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
123
112 IPC::ResponseBuilder rb{ctx, 2}; 124 IPC::ResponseBuilder rb{ctx, 2};
113 rb.Push(RESULT_SUCCESS); 125 rb.Push(RESULT_SUCCESS);
114} 126}
115 127
116void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { 128void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
117 LOG_WARNING(Service_AM, "(STUBBED) called"); 129 LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume);
118 IPC::ResponseBuilder rb{ctx, 3}; 130 IPC::ResponseBuilder rb{ctx, 3};
119 rb.Push(RESULT_SUCCESS); 131 rb.Push(RESULT_SUCCESS);
120 rb.Push(volume); 132 rb.Push(main_applet_volume);
121} 133}
122 134
123void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { 135void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
124 LOG_WARNING(Service_AM, "(STUBBED) called"); 136 LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume);
125 IPC::ResponseBuilder rb{ctx, 3}; 137 IPC::ResponseBuilder rb{ctx, 3};
126 rb.Push(RESULT_SUCCESS); 138 rb.Push(RESULT_SUCCESS);
127 rb.Push(volume); 139 rb.Push(library_applet_volume);
140}
141
142void IAudioController::ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx) {
143 struct Parameters {
144 float volume;
145 s64 fade_time_ns;
146 };
147 static_assert(sizeof(Parameters) == 16);
148
149 IPC::RequestParser rp{ctx};
150 const auto parameters = rp.PopRaw<Parameters>();
151
152 LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume,
153 parameters.fade_time_ns);
154
155 main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume);
156 fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns};
157
158 IPC::ResponseBuilder rb{ctx, 2};
159 rb.Push(RESULT_SUCCESS);
160}
161
162void IAudioController::SetTransparentAudioRate(Kernel::HLERequestContext& ctx) {
163 IPC::RequestParser rp{ctx};
164 const float transparent_volume_rate_tmp = rp.Pop<float>();
165
166 LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp);
167
168 // Clamp volume range to 0-100%.
169 transparent_volume_rate =
170 std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume);
171
172 IPC::ResponseBuilder rb{ctx, 2};
173 rb.Push(RESULT_SUCCESS);
128} 174}
129 175
130IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") { 176IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
@@ -169,7 +215,21 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
169 215
170IDisplayController::~IDisplayController() = default; 216IDisplayController::~IDisplayController() = default;
171 217
172IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {} 218IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
219 // clang-format off
220 static const FunctionInfo functions[] = {
221 {0, nullptr, "NotifyMessageToHomeMenuForDebug"},
222 {1, nullptr, "OpenMainApplication"},
223 {10, nullptr, "EmulateButtonEvent"},
224 {20, nullptr, "InvalidateTransitionLayer"},
225 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
226 {40, nullptr, "GetAppletResourceUsageInfo"},
227 };
228 // clang-format on
229
230 RegisterHandlers(functions);
231}
232
173IDebugFunctions::~IDebugFunctions() = default; 233IDebugFunctions::~IDebugFunctions() = default;
174 234
175ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 235ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
@@ -179,8 +239,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
179 {0, nullptr, "Exit"}, 239 {0, nullptr, "Exit"},
180 {1, &ISelfController::LockExit, "LockExit"}, 240 {1, &ISelfController::LockExit, "LockExit"},
181 {2, &ISelfController::UnlockExit, "UnlockExit"}, 241 {2, &ISelfController::UnlockExit, "UnlockExit"},
182 {3, nullptr, "EnterFatalSection"}, 242 {3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
183 {4, nullptr, "LeaveFatalSection"}, 243 {4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"},
184 {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, 244 {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
185 {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, 245 {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
186 {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"}, 246 {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
@@ -225,41 +285,54 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
225 285
226ISelfController::~ISelfController() = default; 286ISelfController::~ISelfController() = default;
227 287
228void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { 288void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
229 // Takes 3 input u8s with each field located immediately after the previous
230 // u8, these are bool flags. No output.
231 LOG_WARNING(Service_AM, "(STUBBED) called"); 289 LOG_WARNING(Service_AM, "(STUBBED) called");
232 290
233 IPC::RequestParser rp{ctx}; 291 IPC::ResponseBuilder rb{ctx, 2};
292 rb.Push(RESULT_SUCCESS);
293}
234 294
235 struct FocusHandlingModeParams { 295void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
236 u8 unknown0; 296 LOG_WARNING(Service_AM, "(STUBBED) called");
237 u8 unknown1;
238 u8 unknown2;
239 };
240 auto flags = rp.PopRaw<FocusHandlingModeParams>();
241 297
242 IPC::ResponseBuilder rb{ctx, 2}; 298 IPC::ResponseBuilder rb{ctx, 2};
243 rb.Push(RESULT_SUCCESS); 299 rb.Push(RESULT_SUCCESS);
244} 300}
245 301
246void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) { 302void ISelfController::EnterFatalSection(Kernel::HLERequestContext& ctx) {
247 LOG_WARNING(Service_AM, "(STUBBED) called"); 303 ++num_fatal_sections_entered;
304 LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", num_fatal_sections_entered);
248 305
249 IPC::ResponseBuilder rb{ctx, 2}; 306 IPC::ResponseBuilder rb{ctx, 2};
250 rb.Push(RESULT_SUCCESS); 307 rb.Push(RESULT_SUCCESS);
251} 308}
252 309
253void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) { 310void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) {
254 IPC::RequestParser rp{ctx}; 311 LOG_DEBUG(Service_AM, "called.");
255 312
256 bool flag = rp.Pop<bool>(); 313 // Entry and exit of fatal sections must be balanced.
257 LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); 314 if (num_fatal_sections_entered == 0) {
315 IPC::ResponseBuilder rb{ctx, 2};
316 rb.Push(ResultCode{ErrorModule::AM, 512});
317 return;
318 }
319
320 --num_fatal_sections_entered;
258 321
259 IPC::ResponseBuilder rb{ctx, 2}; 322 IPC::ResponseBuilder rb{ctx, 2};
260 rb.Push(RESULT_SUCCESS); 323 rb.Push(RESULT_SUCCESS);
261} 324}
262 325
326void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
327 LOG_WARNING(Service_AM, "(STUBBED) called");
328
329 launchable_event.writable->Signal();
330
331 IPC::ResponseBuilder rb{ctx, 2, 1};
332 rb.Push(RESULT_SUCCESS);
333 rb.PushCopyObjects(launchable_event.readable);
334}
335
263void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { 336void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
264 LOG_WARNING(Service_AM, "(STUBBED) called"); 337 LOG_WARNING(Service_AM, "(STUBBED) called");
265 338
@@ -277,40 +350,52 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
277 rb.Push(RESULT_SUCCESS); 350 rb.Push(RESULT_SUCCESS);
278} 351}
279 352
280void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { 353void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) {
281 // Takes 3 input u8s with each field located immediately after the previous
282 // u8, these are bool flags. No output.
283 IPC::RequestParser rp{ctx}; 354 IPC::RequestParser rp{ctx};
284 355
285 bool enabled = rp.Pop<bool>(); 356 bool flag = rp.Pop<bool>();
286 LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); 357 LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
287 358
288 IPC::ResponseBuilder rb{ctx, 2}; 359 IPC::ResponseBuilder rb{ctx, 2};
289 rb.Push(RESULT_SUCCESS); 360 rb.Push(RESULT_SUCCESS);
290} 361}
291 362
292void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { 363void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
293 LOG_WARNING(Service_AM, "(STUBBED) called"); 364 // Takes 3 input u8s with each field located immediately after the previous
365 // u8, these are bool flags. No output.
366 IPC::RequestParser rp{ctx};
367
368 struct FocusHandlingModeParams {
369 u8 unknown0;
370 u8 unknown1;
371 u8 unknown2;
372 };
373 const auto flags = rp.PopRaw<FocusHandlingModeParams>();
374
375 LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}",
376 flags.unknown0, flags.unknown1, flags.unknown2);
294 377
295 IPC::ResponseBuilder rb{ctx, 2}; 378 IPC::ResponseBuilder rb{ctx, 2};
296 rb.Push(RESULT_SUCCESS); 379 rb.Push(RESULT_SUCCESS);
297} 380}
298 381
299void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { 382void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
300 LOG_WARNING(Service_AM, "(STUBBED) called"); 383 LOG_WARNING(Service_AM, "(STUBBED) called");
301 384
302 IPC::ResponseBuilder rb{ctx, 2}; 385 IPC::ResponseBuilder rb{ctx, 2};
303 rb.Push(RESULT_SUCCESS); 386 rb.Push(RESULT_SUCCESS);
304} 387}
305 388
306void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { 389void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
307 LOG_WARNING(Service_AM, "(STUBBED) called"); 390 // Takes 3 input u8s with each field located immediately after the previous
391 // u8, these are bool flags. No output.
392 IPC::RequestParser rp{ctx};
308 393
309 launchable_event.writable->Signal(); 394 bool enabled = rp.Pop<bool>();
395 LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
310 396
311 IPC::ResponseBuilder rb{ctx, 2, 1}; 397 IPC::ResponseBuilder rb{ctx, 2};
312 rb.Push(RESULT_SUCCESS); 398 rb.Push(RESULT_SUCCESS);
313 rb.PushCopyObjects(launchable_event.readable);
314} 399}
315 400
316void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) { 401void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) {
@@ -847,19 +932,19 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
847 rp.SetCurrentOffset(3); 932 rp.SetCurrentOffset(3);
848 const auto handle{rp.Pop<Kernel::Handle>()}; 933 const auto handle{rp.Pop<Kernel::Handle>()};
849 934
850 const auto shared_mem = 935 const auto transfer_mem =
851 Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>( 936 Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(
852 handle); 937 handle);
853 938
854 if (shared_mem == nullptr) { 939 if (transfer_mem == nullptr) {
855 LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); 940 LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle);
856 IPC::ResponseBuilder rb{ctx, 2}; 941 IPC::ResponseBuilder rb{ctx, 2};
857 rb.Push(ResultCode(-1)); 942 rb.Push(ResultCode(-1));
858 return; 943 return;
859 } 944 }
860 945
861 const u8* mem_begin = shared_mem->GetPointer(); 946 const u8* const mem_begin = transfer_mem->GetPointer();
862 const u8* mem_end = mem_begin + shared_mem->GetSize(); 947 const u8* const mem_end = mem_begin + transfer_mem->GetSize();
863 std::vector<u8> memory{mem_begin, mem_end}; 948 std::vector<u8> memory{mem_begin, mem_end};
864 949
865 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 950 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b6113cfdd..991b7d47c 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <chrono>
7#include <memory> 8#include <memory>
8#include <queue> 9#include <queue>
9#include "core/hle/kernel/writable_event.h" 10#include "core/hle/kernel/writable_event.h"
@@ -81,8 +82,21 @@ private:
81 void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx); 82 void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx);
82 void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx); 83 void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
83 void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx); 84 void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
85 void ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx);
86 void SetTransparentAudioRate(Kernel::HLERequestContext& ctx);
84 87
85 u32 volume{100}; 88 static constexpr float min_allowed_volume = 0.0f;
89 static constexpr float max_allowed_volume = 1.0f;
90
91 float main_applet_volume{0.25f};
92 float library_applet_volume{max_allowed_volume};
93 float transparent_volume_rate{min_allowed_volume};
94
95 // Volume transition fade time in nanoseconds.
96 // e.g. If the main applet volume was 0% and was changed to 50%
97 // with a fade of 50ns, then over the course of 50ns,
98 // the volume will gradually fade up to 50%
99 std::chrono::nanoseconds fade_time_ns{0};
86}; 100};
87 101
88class IDisplayController final : public ServiceFramework<IDisplayController> { 102class IDisplayController final : public ServiceFramework<IDisplayController> {
@@ -103,17 +117,19 @@ public:
103 ~ISelfController() override; 117 ~ISelfController() override;
104 118
105private: 119private:
106 void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
107 void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx);
108 void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx);
109 void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx);
110 void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
111 void LockExit(Kernel::HLERequestContext& ctx); 120 void LockExit(Kernel::HLERequestContext& ctx);
112 void UnlockExit(Kernel::HLERequestContext& ctx); 121 void UnlockExit(Kernel::HLERequestContext& ctx);
122 void EnterFatalSection(Kernel::HLERequestContext& ctx);
123 void LeaveFatalSection(Kernel::HLERequestContext& ctx);
113 void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx); 124 void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
125 void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
126 void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx);
127 void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx);
128 void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
129 void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx);
130 void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
114 void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx); 131 void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx);
115 void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); 132 void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
116 void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
117 void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); 133 void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
118 void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); 134 void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
119 void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); 135 void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
@@ -121,6 +137,7 @@ private:
121 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 137 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
122 Kernel::EventPair launchable_event; 138 Kernel::EventPair launchable_event;
123 u32 idle_time_detection_extension = 0; 139 u32 idle_time_detection_extension = 0;
140 u64 num_fatal_sections_entered = 0;
124}; 141};
125 142
126class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 143class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 088410564..e5daefdde 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 {
@@ -33,7 +30,6 @@ public:
33 30
34 RegisterHandlers(functions); 31 RegisterHandlers(functions);
35 } 32 }
36 ~IAudioIn() = default;
37}; 33};
38 34
39AudInU::AudInU() : ServiceFramework("audin:u") { 35AudInU::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..39acb7b23 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -150,7 +150,6 @@ private:
150 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { 150 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
151 LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); 151 LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
152 152
153 IPC::RequestParser rp{ctx};
154 const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; 153 const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
155 const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; 154 const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
156 155
@@ -194,12 +193,9 @@ private:
194void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { 193void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
195 LOG_DEBUG(Service_Audio, "called"); 194 LOG_DEBUG(Service_Audio, "called");
196 195
197 IPC::RequestParser rp{ctx};
198
199 ctx.WriteBuffer(DefaultDevice); 196 ctx.WriteBuffer(DefaultDevice);
200 197
201 IPC::ResponseBuilder rb{ctx, 3}; 198 IPC::ResponseBuilder rb{ctx, 3};
202
203 rb.Push(RESULT_SUCCESS); 199 rb.Push(RESULT_SUCCESS);
204 rb.Push<u32>(1); // Amount of audio devices 200 rb.Push<u32>(1); // Amount of audio devices
205} 201}
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/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 377e12cfa..cb4a1160d 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9 9
10#include <opus.h> 10#include <opus.h>
11#include <opus_multistream.h>
11 12
12#include "common/assert.h" 13#include "common/assert.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
@@ -18,12 +19,12 @@
18namespace Service::Audio { 19namespace Service::Audio {
19namespace { 20namespace {
20struct OpusDeleter { 21struct OpusDeleter {
21 void operator()(void* ptr) const { 22 void operator()(OpusMSDecoder* ptr) const {
22 operator delete(ptr); 23 opus_multistream_decoder_destroy(ptr);
23 } 24 }
24}; 25};
25 26
26using OpusDecoderPtr = std::unique_ptr<OpusDecoder, OpusDeleter>; 27using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>;
27 28
28struct OpusPacketHeader { 29struct OpusPacketHeader {
29 // Packet size in bytes. 30 // Packet size in bytes.
@@ -33,7 +34,7 @@ struct OpusPacketHeader {
33}; 34};
34static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size"); 35static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
35 36
36class OpusDecoderStateBase { 37class OpusDecoderState {
37public: 38public:
38 /// Describes extra behavior that may be asked of the decoding context. 39 /// Describes extra behavior that may be asked of the decoding context.
39 enum class ExtraBehavior { 40 enum class ExtraBehavior {
@@ -49,22 +50,13 @@ public:
49 Enabled, 50 Enabled,
50 }; 51 };
51 52
52 virtual ~OpusDecoderStateBase() = default;
53
54 // Decodes interleaved Opus packets. Optionally allows reporting time taken to
55 // perform the decoding, as well as any relevant extra behavior.
56 virtual void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
57 ExtraBehavior extra_behavior) = 0;
58};
59
60// Represents the decoder state for a non-multistream decoder.
61class OpusDecoderState final : public OpusDecoderStateBase {
62public:
63 explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count) 53 explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
64 : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {} 54 : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
65 55
56 // Decodes interleaved Opus packets. Optionally allows reporting time taken to
57 // perform the decoding, as well as any relevant extra behavior.
66 void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time, 58 void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
67 ExtraBehavior extra_behavior) override { 59 ExtraBehavior extra_behavior) {
68 if (perf_time == PerfTime::Disabled) { 60 if (perf_time == PerfTime::Disabled) {
69 DecodeInterleavedHelper(ctx, nullptr, extra_behavior); 61 DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
70 } else { 62 } else {
@@ -135,7 +127,7 @@ private:
135 127
136 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); 128 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
137 const auto out_sample_count = 129 const auto out_sample_count =
138 opus_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0); 130 opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
139 if (out_sample_count < 0) { 131 if (out_sample_count < 0) {
140 LOG_ERROR(Audio, 132 LOG_ERROR(Audio,
141 "Incorrect sample count received from opus_decode, " 133 "Incorrect sample count received from opus_decode, "
@@ -158,7 +150,7 @@ private:
158 void ResetDecoderContext() { 150 void ResetDecoderContext() {
159 ASSERT(decoder != nullptr); 151 ASSERT(decoder != nullptr);
160 152
161 opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE); 153 opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
162 } 154 }
163 155
164 OpusDecoderPtr decoder; 156 OpusDecoderPtr decoder;
@@ -168,7 +160,7 @@ private:
168 160
169class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 161class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
170public: 162public:
171 explicit IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoderStateBase> decoder_state) 163 explicit IHardwareOpusDecoderManager(OpusDecoderState decoder_state)
172 : ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} { 164 : ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} {
173 // clang-format off 165 // clang-format off
174 static const FunctionInfo functions[] = { 166 static const FunctionInfo functions[] = {
@@ -190,35 +182,51 @@ private:
190 void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) { 182 void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
191 LOG_DEBUG(Audio, "called"); 183 LOG_DEBUG(Audio, "called");
192 184
193 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Disabled, 185 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled,
194 OpusDecoderStateBase::ExtraBehavior::None); 186 OpusDecoderState::ExtraBehavior::None);
195 } 187 }
196 188
197 void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) { 189 void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
198 LOG_DEBUG(Audio, "called"); 190 LOG_DEBUG(Audio, "called");
199 191
200 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled, 192 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled,
201 OpusDecoderStateBase::ExtraBehavior::None); 193 OpusDecoderState::ExtraBehavior::None);
202 } 194 }
203 195
204 void DecodeInterleaved(Kernel::HLERequestContext& ctx) { 196 void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
205 LOG_DEBUG(Audio, "called"); 197 LOG_DEBUG(Audio, "called");
206 198
207 IPC::RequestParser rp{ctx}; 199 IPC::RequestParser rp{ctx};
208 const auto extra_behavior = rp.Pop<bool>() 200 const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
209 ? OpusDecoderStateBase::ExtraBehavior::ResetContext 201 : OpusDecoderState::ExtraBehavior::None;
210 : OpusDecoderStateBase::ExtraBehavior::None;
211 202
212 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled, 203 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
213 extra_behavior);
214 } 204 }
215 205
216 std::unique_ptr<OpusDecoderStateBase> decoder_state; 206 OpusDecoderState decoder_state;
217}; 207};
218 208
219std::size_t WorkerBufferSize(u32 channel_count) { 209std::size_t WorkerBufferSize(u32 channel_count) {
220 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 210 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
221 return opus_decoder_get_size(static_cast<int>(channel_count)); 211 constexpr int num_streams = 1;
212 const int num_stereo_streams = channel_count == 2 ? 1 : 0;
213 return opus_multistream_decoder_get_size(num_streams, num_stereo_streams);
214}
215
216// Creates the mapping table that maps the input channels to the particular
217// output channels. In the stereo case, we map the left and right input channels
218// to the left and right output channels respectively.
219//
220// However, in the monophonic case, we only map the one available channel
221// to the sole output channel. We specify 255 for the would-be right channel
222// as this is a special value defined by Opus to indicate to the decoder to
223// ignore that channel.
224std::array<u8, 2> CreateMappingTable(u32 channel_count) {
225 if (channel_count == 2) {
226 return {{0, 1}};
227 }
228
229 return {{0, 255}};
222} 230}
223} // Anonymous namespace 231} // Anonymous namespace
224 232
@@ -259,9 +267,15 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
259 const std::size_t worker_sz = WorkerBufferSize(channel_count); 267 const std::size_t worker_sz = WorkerBufferSize(channel_count);
260 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); 268 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
261 269
262 OpusDecoderPtr decoder{static_cast<OpusDecoder*>(operator new(worker_sz))}; 270 const int num_stereo_streams = channel_count == 2 ? 1 : 0;
263 if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) { 271 const auto mapping_table = CreateMappingTable(channel_count);
264 LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err); 272
273 int error = 0;
274 OpusDecoderPtr decoder{
275 opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1,
276 num_stereo_streams, mapping_table.data(), &error)};
277 if (error != OPUS_OK || decoder == nullptr) {
278 LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
265 IPC::ResponseBuilder rb{ctx, 2}; 279 IPC::ResponseBuilder rb{ctx, 2};
266 // TODO(ogniK): Use correct error code 280 // TODO(ogniK): Use correct error code
267 rb.Push(ResultCode(-1)); 281 rb.Push(ResultCode(-1));
@@ -271,7 +285,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
271 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 285 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
272 rb.Push(RESULT_SUCCESS); 286 rb.Push(RESULT_SUCCESS);
273 rb.PushIpcInterface<IHardwareOpusDecoderManager>( 287 rb.PushIpcInterface<IHardwareOpusDecoderManager>(
274 std::make_unique<OpusDecoderState>(std::move(decoder), sample_rate, channel_count)); 288 OpusDecoderState{std::move(decoder), sample_rate, channel_count});
275} 289}
276 290
277HwOpus::HwOpus() : ServiceFramework("hwopus") { 291HwOpus::HwOpus() : ServiceFramework("hwopus") {
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 770590d0b..2c229bcad 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -25,21 +25,34 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
25Module::Interface::~Interface() = default; 25Module::Interface::~Interface() = default;
26 26
27struct FatalInfo { 27struct FatalInfo {
28 std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or 28 enum class Architecture : s32 {
29 // not(find a game which has non zero valeus) 29 AArch64,
30 u64_le unk0{}; 30 AArch32,
31 u64_le unk1{}; 31 };
32 u64_le unk2{}; 32
33 u64_le unk3{}; 33 const char* ArchAsString() const {
34 u64_le unk4{}; 34 return arch == Architecture::AArch64 ? "AArch64" : "AArch32";
35 u64_le unk5{}; 35 }
36 u64_le unk6{}; 36
37 std::array<u64_le, 31> registers{};
38 u64_le sp{};
39 u64_le pc{};
40 u64_le pstate{};
41 u64_le afsr0{};
42 u64_le afsr1{};
43 u64_le esr{};
44 u64_le far{};
37 45
38 std::array<u64_le, 32> backtrace{}; 46 std::array<u64_le, 32> backtrace{};
39 u64_le unk7{}; 47 u64_le program_entry_point{};
40 u64_le unk8{}; 48
49 // Bit flags that indicate which registers have been set with values
50 // for this context. The service itself uses these to determine which
51 // registers to specifically print out.
52 u64_le set_flags{};
53
41 u32_le backtrace_size{}; 54 u32_le backtrace_size{};
42 u32_le unk9{}; 55 Architecture arch{};
43 u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding? 56 u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding?
44}; 57};
45static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size"); 58static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");
@@ -52,36 +65,36 @@ enum class FatalType : u32 {
52 65
53static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { 66static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
54 const auto title_id = Core::CurrentProcess()->GetTitleID(); 67 const auto title_id = Core::CurrentProcess()->GetTitleID();
55 std::string crash_report = 68 std::string crash_report = fmt::format(
56 fmt::format("Yuzu {}-{} crash report\n" 69 "Yuzu {}-{} crash report\n"
57 "Title ID: {:016x}\n" 70 "Title ID: {:016x}\n"
58 "Result: 0x{:X} ({:04}-{:04d})\n" 71 "Result: 0x{:X} ({:04}-{:04d})\n"
59 "\n", 72 "Set flags: 0x{:16X}\n"
60 Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw, 73 "Program entry point: 0x{:16X}\n"
61 2000 + static_cast<u32>(error_code.module.Value()), 74 "\n",
62 static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7); 75 Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
76 2000 + static_cast<u32>(error_code.module.Value()),
77 static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point);
63 if (info.backtrace_size != 0x0) { 78 if (info.backtrace_size != 0x0) {
64 crash_report += "Registers:\n"; 79 crash_report += "Registers:\n";
65 // TODO(ogniK): This is just a guess, find a game which actually has non zero values
66 for (size_t i = 0; i < info.registers.size(); i++) { 80 for (size_t i = 0; i < info.registers.size(); i++) {
67 crash_report += 81 crash_report +=
68 fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]); 82 fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]);
69 } 83 }
70 crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0); 84 crash_report += fmt::format(" SP: {:016x}\n", info.sp);
71 crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1); 85 crash_report += fmt::format(" PC: {:016x}\n", info.pc);
72 crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2); 86 crash_report += fmt::format(" PSTATE: {:016x}\n", info.pstate);
73 crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3); 87 crash_report += fmt::format(" AFSR0: {:016x}\n", info.afsr0);
74 crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4); 88 crash_report += fmt::format(" AFSR1: {:016x}\n", info.afsr1);
75 crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5); 89 crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
76 crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6); 90 crash_report += fmt::format(" FAR: {:016x}\n", info.far);
77 crash_report += "\nBacktrace:\n"; 91 crash_report += "\nBacktrace:\n";
78 for (size_t i = 0; i < info.backtrace_size; i++) { 92 for (size_t i = 0; i < info.backtrace_size; i++) {
79 crash_report += 93 crash_report +=
80 fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]); 94 fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
81 } 95 }
82 crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7); 96
83 crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8); 97 crash_report += fmt::format("Architecture: {}\n", info.ArchAsString());
84 crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9);
85 crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10); 98 crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10);
86 } 99 }
87 100
@@ -125,13 +138,13 @@ static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const F
125 case FatalType::ErrorReport: 138 case FatalType::ErrorReport:
126 GenerateErrorReport(error_code, info); 139 GenerateErrorReport(error_code, info);
127 break; 140 break;
128 }; 141 }
129} 142}
130 143
131void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { 144void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
132 LOG_ERROR(Service_Fatal, "called"); 145 LOG_ERROR(Service_Fatal, "called");
133 IPC::RequestParser rp{ctx}; 146 IPC::RequestParser rp{ctx};
134 auto error_code = rp.Pop<ResultCode>(); 147 const auto error_code = rp.Pop<ResultCode>();
135 148
136 ThrowFatalError(error_code, FatalType::ErrorScreen, {}); 149 ThrowFatalError(error_code, FatalType::ErrorScreen, {});
137 IPC::ResponseBuilder rb{ctx, 2}; 150 IPC::ResponseBuilder rb{ctx, 2};
@@ -141,8 +154,8 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
141void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { 154void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
142 LOG_ERROR(Service_Fatal, "called"); 155 LOG_ERROR(Service_Fatal, "called");
143 IPC::RequestParser rp(ctx); 156 IPC::RequestParser rp(ctx);
144 auto error_code = rp.Pop<ResultCode>(); 157 const auto error_code = rp.Pop<ResultCode>();
145 auto fatal_type = rp.PopEnum<FatalType>(); 158 const auto fatal_type = rp.PopEnum<FatalType>();
146 159
147 ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy 160 ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy
148 IPC::ResponseBuilder rb{ctx, 2}; 161 IPC::ResponseBuilder rb{ctx, 2};
@@ -152,9 +165,9 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
152void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { 165void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
153 LOG_ERROR(Service_Fatal, "called"); 166 LOG_ERROR(Service_Fatal, "called");
154 IPC::RequestParser rp(ctx); 167 IPC::RequestParser rp(ctx);
155 auto error_code = rp.Pop<ResultCode>(); 168 const auto error_code = rp.Pop<ResultCode>();
156 auto fatal_type = rp.PopEnum<FatalType>(); 169 const auto fatal_type = rp.PopEnum<FatalType>();
157 auto fatal_info = ctx.ReadBuffer(); 170 const auto fatal_info = ctx.ReadBuffer();
158 FatalInfo info{}; 171 FatalInfo info{};
159 172
160 ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!"); 173 ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index c6da2df43..4c2b371c3 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) {
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 6fd5e7b23..7cfc0d902 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
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 54959edd8..657baddb8 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -315,61 +315,53 @@ public:
315 void CreateFile(Kernel::HLERequestContext& ctx) { 315 void CreateFile(Kernel::HLERequestContext& ctx) {
316 IPC::RequestParser rp{ctx}; 316 IPC::RequestParser rp{ctx};
317 317
318 auto file_buffer = ctx.ReadBuffer(); 318 const auto file_buffer = ctx.ReadBuffer();
319 std::string name = Common::StringFromBuffer(file_buffer); 319 const std::string name = Common::StringFromBuffer(file_buffer);
320 320
321 u64 mode = rp.Pop<u64>(); 321 const u64 mode = rp.Pop<u64>();
322 u32 size = rp.Pop<u32>(); 322 const u32 size = rp.Pop<u32>();
323 323
324 LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); 324 LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, mode, size);
325 325
326 IPC::ResponseBuilder rb{ctx, 2}; 326 IPC::ResponseBuilder rb{ctx, 2};
327 rb.Push(backend.CreateFile(name, size)); 327 rb.Push(backend.CreateFile(name, size));
328 } 328 }
329 329
330 void DeleteFile(Kernel::HLERequestContext& ctx) { 330 void DeleteFile(Kernel::HLERequestContext& ctx) {
331 IPC::RequestParser rp{ctx}; 331 const auto file_buffer = ctx.ReadBuffer();
332 332 const std::string name = Common::StringFromBuffer(file_buffer);
333 auto file_buffer = ctx.ReadBuffer();
334 std::string name = Common::StringFromBuffer(file_buffer);
335 333
336 LOG_DEBUG(Service_FS, "called file {}", name); 334 LOG_DEBUG(Service_FS, "called. file={}", name);
337 335
338 IPC::ResponseBuilder rb{ctx, 2}; 336 IPC::ResponseBuilder rb{ctx, 2};
339 rb.Push(backend.DeleteFile(name)); 337 rb.Push(backend.DeleteFile(name));
340 } 338 }
341 339
342 void CreateDirectory(Kernel::HLERequestContext& ctx) { 340 void CreateDirectory(Kernel::HLERequestContext& ctx) {
343 IPC::RequestParser rp{ctx}; 341 const auto file_buffer = ctx.ReadBuffer();
344 342 const std::string name = Common::StringFromBuffer(file_buffer);
345 auto file_buffer = ctx.ReadBuffer();
346 std::string name = Common::StringFromBuffer(file_buffer);
347 343
348 LOG_DEBUG(Service_FS, "called directory {}", name); 344 LOG_DEBUG(Service_FS, "called. directory={}", name);
349 345
350 IPC::ResponseBuilder rb{ctx, 2}; 346 IPC::ResponseBuilder rb{ctx, 2};
351 rb.Push(backend.CreateDirectory(name)); 347 rb.Push(backend.CreateDirectory(name));
352 } 348 }
353 349
354 void DeleteDirectory(Kernel::HLERequestContext& ctx) { 350 void DeleteDirectory(Kernel::HLERequestContext& ctx) {
355 const IPC::RequestParser rp{ctx};
356
357 const auto file_buffer = ctx.ReadBuffer(); 351 const auto file_buffer = ctx.ReadBuffer();
358 std::string name = Common::StringFromBuffer(file_buffer); 352 const std::string name = Common::StringFromBuffer(file_buffer);
359 353
360 LOG_DEBUG(Service_FS, "called directory {}", name); 354 LOG_DEBUG(Service_FS, "called. directory={}", name);
361 355
362 IPC::ResponseBuilder rb{ctx, 2}; 356 IPC::ResponseBuilder rb{ctx, 2};
363 rb.Push(backend.DeleteDirectory(name)); 357 rb.Push(backend.DeleteDirectory(name));
364 } 358 }
365 359
366 void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) { 360 void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
367 const IPC::RequestParser rp{ctx};
368
369 const auto file_buffer = ctx.ReadBuffer(); 361 const auto file_buffer = ctx.ReadBuffer();
370 std::string name = Common::StringFromBuffer(file_buffer); 362 const std::string name = Common::StringFromBuffer(file_buffer);
371 363
372 LOG_DEBUG(Service_FS, "called directory {}", name); 364 LOG_DEBUG(Service_FS, "called. directory={}", name);
373 365
374 IPC::ResponseBuilder rb{ctx, 2}; 366 IPC::ResponseBuilder rb{ctx, 2};
375 rb.Push(backend.DeleteDirectoryRecursively(name)); 367 rb.Push(backend.DeleteDirectoryRecursively(name));
@@ -386,18 +378,16 @@ public:
386 } 378 }
387 379
388 void RenameFile(Kernel::HLERequestContext& ctx) { 380 void RenameFile(Kernel::HLERequestContext& ctx) {
389 IPC::RequestParser rp{ctx};
390
391 std::vector<u8> buffer; 381 std::vector<u8> buffer;
392 buffer.resize(ctx.BufferDescriptorX()[0].Size()); 382 buffer.resize(ctx.BufferDescriptorX()[0].Size());
393 Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size()); 383 Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
394 std::string src_name = Common::StringFromBuffer(buffer); 384 const std::string src_name = Common::StringFromBuffer(buffer);
395 385
396 buffer.resize(ctx.BufferDescriptorX()[1].Size()); 386 buffer.resize(ctx.BufferDescriptorX()[1].Size());
397 Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size()); 387 Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
398 std::string dst_name = Common::StringFromBuffer(buffer); 388 const std::string dst_name = Common::StringFromBuffer(buffer);
399 389
400 LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); 390 LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
401 391
402 IPC::ResponseBuilder rb{ctx, 2}; 392 IPC::ResponseBuilder rb{ctx, 2};
403 rb.Push(backend.RenameFile(src_name, dst_name)); 393 rb.Push(backend.RenameFile(src_name, dst_name));
@@ -406,12 +396,12 @@ public:
406 void OpenFile(Kernel::HLERequestContext& ctx) { 396 void OpenFile(Kernel::HLERequestContext& ctx) {
407 IPC::RequestParser rp{ctx}; 397 IPC::RequestParser rp{ctx};
408 398
409 auto file_buffer = ctx.ReadBuffer(); 399 const auto file_buffer = ctx.ReadBuffer();
410 std::string name = Common::StringFromBuffer(file_buffer); 400 const std::string name = Common::StringFromBuffer(file_buffer);
411 401
412 auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); 402 const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
413 403
414 LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); 404 LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, static_cast<u32>(mode));
415 405
416 auto result = backend.OpenFile(name, mode); 406 auto result = backend.OpenFile(name, mode);
417 if (result.Failed()) { 407 if (result.Failed()) {
@@ -430,13 +420,13 @@ public:
430 void OpenDirectory(Kernel::HLERequestContext& ctx) { 420 void OpenDirectory(Kernel::HLERequestContext& ctx) {
431 IPC::RequestParser rp{ctx}; 421 IPC::RequestParser rp{ctx};
432 422
433 auto file_buffer = ctx.ReadBuffer(); 423 const auto file_buffer = ctx.ReadBuffer();
434 std::string name = Common::StringFromBuffer(file_buffer); 424 const std::string name = Common::StringFromBuffer(file_buffer);
435 425
436 // TODO(Subv): Implement this filter. 426 // TODO(Subv): Implement this filter.
437 u32 filter_flags = rp.Pop<u32>(); 427 const u32 filter_flags = rp.Pop<u32>();
438 428
439 LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); 429 LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags);
440 430
441 auto result = backend.OpenDirectory(name); 431 auto result = backend.OpenDirectory(name);
442 if (result.Failed()) { 432 if (result.Failed()) {
@@ -453,12 +443,10 @@ public:
453 } 443 }
454 444
455 void GetEntryType(Kernel::HLERequestContext& ctx) { 445 void GetEntryType(Kernel::HLERequestContext& ctx) {
456 IPC::RequestParser rp{ctx}; 446 const auto file_buffer = ctx.ReadBuffer();
457 447 const std::string name = Common::StringFromBuffer(file_buffer);
458 auto file_buffer = ctx.ReadBuffer();
459 std::string name = Common::StringFromBuffer(file_buffer);
460 448
461 LOG_DEBUG(Service_FS, "called file {}", name); 449 LOG_DEBUG(Service_FS, "called. file={}", name);
462 450
463 auto result = backend.GetEntryType(name); 451 auto result = backend.GetEntryType(name);
464 if (result.Failed()) { 452 if (result.Failed()) {
@@ -616,7 +604,9 @@ private:
616 u64_le save_id; 604 u64_le save_id;
617 u64_le title_id; 605 u64_le title_id;
618 u64_le save_image_size; 606 u64_le save_image_size;
619 INSERT_PADDING_BYTES(0x28); 607 u16_le index;
608 FileSys::SaveDataRank rank;
609 INSERT_PADDING_BYTES(0x25);
620 }; 610 };
621 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); 611 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
622 612
@@ -733,7 +723,10 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
733FSP_SRV::~FSP_SRV() = default; 723FSP_SRV::~FSP_SRV() = default;
734 724
735void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) { 725void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
736 LOG_WARNING(Service_FS, "(STUBBED) called"); 726 IPC::RequestParser rp{ctx};
727 current_process_id = rp.Pop<u64>();
728
729 LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
737 730
738 IPC::ResponseBuilder rb{ctx, 2}; 731 IPC::ResponseBuilder rb{ctx, 2};
739 rb.Push(RESULT_SUCCESS); 732 rb.Push(RESULT_SUCCESS);
@@ -776,16 +769,17 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
776} 769}
777 770
778void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { 771void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
779 IPC::RequestParser rp{ctx}; 772 LOG_INFO(Service_FS, "called.");
780 773
781 auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>(); 774 struct Parameters {
782 auto unk = rp.Pop<u32>(); 775 FileSys::SaveDataSpaceId save_data_space_id;
783 LOG_INFO(Service_FS, "called with unknown={:08X}", unk); 776 FileSys::SaveDataDescriptor descriptor;
784 777 };
785 auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
786 778
787 auto dir = OpenSaveData(space_id, save_struct); 779 IPC::RequestParser rp{ctx};
780 const auto parameters = rp.PopRaw<Parameters>();
788 781
782 auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
789 if (dir.Failed()) { 783 if (dir.Failed()) {
790 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 784 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
791 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); 785 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 3a5f4e200..d7572ba7a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -32,6 +32,7 @@ private:
32 void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 32 void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
33 33
34 FileSys::VirtualFile romfs; 34 FileSys::VirtualFile romfs;
35 u64 current_process_id = 0;
35}; 36};
36 37
37} // namespace Service::FileSystem 38} // namespace Service::FileSystem
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 929035034..e584b92ec 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -41,20 +41,20 @@ private:
41 struct PadState { 41 struct PadState {
42 union { 42 union {
43 u32_le raw{}; 43 u32_le raw{};
44 BitField<0, 1, u32_le> a; 44 BitField<0, 1, u32> a;
45 BitField<1, 1, u32_le> b; 45 BitField<1, 1, u32> b;
46 BitField<2, 1, u32_le> x; 46 BitField<2, 1, u32> x;
47 BitField<3, 1, u32_le> y; 47 BitField<3, 1, u32> y;
48 BitField<4, 1, u32_le> l; 48 BitField<4, 1, u32> l;
49 BitField<5, 1, u32_le> r; 49 BitField<5, 1, u32> r;
50 BitField<6, 1, u32_le> zl; 50 BitField<6, 1, u32> zl;
51 BitField<7, 1, u32_le> zr; 51 BitField<7, 1, u32> zr;
52 BitField<8, 1, u32_le> plus; 52 BitField<8, 1, u32> plus;
53 BitField<9, 1, u32_le> minus; 53 BitField<9, 1, u32> minus;
54 BitField<10, 1, u32_le> d_left; 54 BitField<10, 1, u32> d_left;
55 BitField<11, 1, u32_le> d_up; 55 BitField<11, 1, u32> d_up;
56 BitField<12, 1, u32_le> d_right; 56 BitField<12, 1, u32> d_right;
57 BitField<13, 1, u32_le> d_down; 57 BitField<13, 1, u32> d_down;
58 }; 58 };
59 }; 59 };
60 static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size"); 60 static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
@@ -62,7 +62,7 @@ private:
62 struct Attributes { 62 struct Attributes {
63 union { 63 union {
64 u32_le raw{}; 64 u32_le raw{};
65 BitField<0, 1, u32_le> connected; 65 BitField<0, 1, u32> connected;
66 }; 66 };
67 }; 67 };
68 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); 68 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 18c7a94e6..4ff50b3cd 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -39,13 +39,13 @@ public:
39 union { 39 union {
40 u32_le raw{}; 40 u32_le raw{};
41 41
42 BitField<0, 1, u32_le> pro_controller; 42 BitField<0, 1, u32> pro_controller;
43 BitField<1, 1, u32_le> handheld; 43 BitField<1, 1, u32> handheld;
44 BitField<2, 1, u32_le> joycon_dual; 44 BitField<2, 1, u32> joycon_dual;
45 BitField<3, 1, u32_le> joycon_left; 45 BitField<3, 1, u32> joycon_left;
46 BitField<4, 1, u32_le> joycon_right; 46 BitField<4, 1, u32> joycon_right;
47 47
48 BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible 48 BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
49 }; 49 };
50 }; 50 };
51 static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size"); 51 static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
@@ -150,43 +150,43 @@ private:
150 union { 150 union {
151 u64_le raw{}; 151 u64_le raw{};
152 // Button states 152 // Button states
153 BitField<0, 1, u64_le> a; 153 BitField<0, 1, u64> a;
154 BitField<1, 1, u64_le> b; 154 BitField<1, 1, u64> b;
155 BitField<2, 1, u64_le> x; 155 BitField<2, 1, u64> x;
156 BitField<3, 1, u64_le> y; 156 BitField<3, 1, u64> y;
157 BitField<4, 1, u64_le> l_stick; 157 BitField<4, 1, u64> l_stick;
158 BitField<5, 1, u64_le> r_stick; 158 BitField<5, 1, u64> r_stick;
159 BitField<6, 1, u64_le> l; 159 BitField<6, 1, u64> l;
160 BitField<7, 1, u64_le> r; 160 BitField<7, 1, u64> r;
161 BitField<8, 1, u64_le> zl; 161 BitField<8, 1, u64> zl;
162 BitField<9, 1, u64_le> zr; 162 BitField<9, 1, u64> zr;
163 BitField<10, 1, u64_le> plus; 163 BitField<10, 1, u64> plus;
164 BitField<11, 1, u64_le> minus; 164 BitField<11, 1, u64> minus;
165 165
166 // D-Pad 166 // D-Pad
167 BitField<12, 1, u64_le> d_left; 167 BitField<12, 1, u64> d_left;
168 BitField<13, 1, u64_le> d_up; 168 BitField<13, 1, u64> d_up;
169 BitField<14, 1, u64_le> d_right; 169 BitField<14, 1, u64> d_right;
170 BitField<15, 1, u64_le> d_down; 170 BitField<15, 1, u64> d_down;
171 171
172 // Left JoyStick 172 // Left JoyStick
173 BitField<16, 1, u64_le> l_stick_left; 173 BitField<16, 1, u64> l_stick_left;
174 BitField<17, 1, u64_le> l_stick_up; 174 BitField<17, 1, u64> l_stick_up;
175 BitField<18, 1, u64_le> l_stick_right; 175 BitField<18, 1, u64> l_stick_right;
176 BitField<19, 1, u64_le> l_stick_down; 176 BitField<19, 1, u64> l_stick_down;
177 177
178 // Right JoyStick 178 // Right JoyStick
179 BitField<20, 1, u64_le> r_stick_left; 179 BitField<20, 1, u64> r_stick_left;
180 BitField<21, 1, u64_le> r_stick_up; 180 BitField<21, 1, u64> r_stick_up;
181 BitField<22, 1, u64_le> r_stick_right; 181 BitField<22, 1, u64> r_stick_right;
182 BitField<23, 1, u64_le> r_stick_down; 182 BitField<23, 1, u64> r_stick_down;
183 183
184 // Not always active? 184 // Not always active?
185 BitField<24, 1, u64_le> left_sl; 185 BitField<24, 1, u64> left_sl;
186 BitField<25, 1, u64_le> left_sr; 186 BitField<25, 1, u64> left_sr;
187 187
188 BitField<26, 1, u64_le> right_sl; 188 BitField<26, 1, u64> right_sl;
189 BitField<27, 1, u64_le> right_sr; 189 BitField<27, 1, u64> right_sr;
190 }; 190 };
191 }; 191 };
192 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); 192 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -200,12 +200,12 @@ private:
200 struct ConnectionState { 200 struct ConnectionState {
201 union { 201 union {
202 u32_le raw{}; 202 u32_le raw{};
203 BitField<0, 1, u32_le> IsConnected; 203 BitField<0, 1, u32> IsConnected;
204 BitField<1, 1, u32_le> IsWired; 204 BitField<1, 1, u32> IsWired;
205 BitField<2, 1, u32_le> IsLeftJoyConnected; 205 BitField<2, 1, u32> IsLeftJoyConnected;
206 BitField<3, 1, u32_le> IsLeftJoyWired; 206 BitField<3, 1, u32> IsLeftJoyWired;
207 BitField<4, 1, u32_le> IsRightJoyConnected; 207 BitField<4, 1, u32> IsRightJoyConnected;
208 BitField<5, 1, u32_le> IsRightJoyWired; 208 BitField<5, 1, u32> IsRightJoyWired;
209 }; 209 };
210 }; 210 };
211 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); 211 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
@@ -240,23 +240,23 @@ private:
240 struct NPadProperties { 240 struct NPadProperties {
241 union { 241 union {
242 s64_le raw{}; 242 s64_le raw{};
243 BitField<11, 1, s64_le> is_vertical; 243 BitField<11, 1, s64> is_vertical;
244 BitField<12, 1, s64_le> is_horizontal; 244 BitField<12, 1, s64> is_horizontal;
245 BitField<13, 1, s64_le> use_plus; 245 BitField<13, 1, s64> use_plus;
246 BitField<14, 1, s64_le> use_minus; 246 BitField<14, 1, s64> use_minus;
247 }; 247 };
248 }; 248 };
249 249
250 struct NPadDevice { 250 struct NPadDevice {
251 union { 251 union {
252 u32_le raw{}; 252 u32_le raw{};
253 BitField<0, 1, s32_le> pro_controller; 253 BitField<0, 1, s32> pro_controller;
254 BitField<1, 1, s32_le> handheld; 254 BitField<1, 1, s32> handheld;
255 BitField<2, 1, s32_le> handheld_left; 255 BitField<2, 1, s32> handheld_left;
256 BitField<3, 1, s32_le> handheld_right; 256 BitField<3, 1, s32> handheld_right;
257 BitField<4, 1, s32_le> joycon_left; 257 BitField<4, 1, s32> joycon_left;
258 BitField<5, 1, s32_le> joycon_right; 258 BitField<5, 1, s32> joycon_right;
259 BitField<6, 1, s32_le> pokeball; 259 BitField<6, 1, s32> pokeball;
260 }; 260 };
261 }; 261 };
262 262
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 012b6e0dd..76fc340e9 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -33,8 +33,8 @@ private:
33 struct Attributes { 33 struct Attributes {
34 union { 34 union {
35 u32 raw{}; 35 u32 raw{};
36 BitField<0, 1, u32_le> start_touch; 36 BitField<0, 1, u32> start_touch;
37 BitField<1, 1, u32_le> end_touch; 37 BitField<1, 1, u32> end_touch;
38 }; 38 };
39 }; 39 };
40 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); 40 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8a6de83a2..63b55758b 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -36,9 +36,9 @@ namespace Service::HID {
36 36
37// Updating period for each HID device. 37// Updating period for each HID device.
38// TODO(ogniK): Find actual polling rate of hid 38// TODO(ogniK): Find actual polling rate of hid
39constexpr u64 pad_update_ticks = Core::Timing::BASE_CLOCK_RATE / 66; 39constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66);
40constexpr u64 accelerometer_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100; 40constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
41constexpr u64 gyroscope_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100; 41constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
42constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 42constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
43 43
44IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") { 44IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
@@ -75,7 +75,7 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
75 // Register update callbacks 75 // Register update callbacks
76 auto& core_timing = Core::System::GetInstance().CoreTiming(); 76 auto& core_timing = Core::System::GetInstance().CoreTiming();
77 pad_update_event = 77 pad_update_event =
78 core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) { 78 core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) {
79 UpdateControllers(userdata, cycles_late); 79 UpdateControllers(userdata, cycles_late);
80 }); 80 });
81 81
@@ -106,7 +106,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
106 rb.PushCopyObjects(shared_mem); 106 rb.PushCopyObjects(shared_mem);
107} 107}
108 108
109void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) { 109void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
110 auto& core_timing = Core::System::GetInstance().CoreTiming(); 110 auto& core_timing = Core::System::GetInstance().CoreTiming();
111 111
112 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); 112 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 7cc58db4c..d3660cad2 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,6 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/hid/controllers/controller_base.h"
8#include "core/hle/service/service.h"
9
7#include "controllers/controller_base.h" 10#include "controllers/controller_base.h"
8#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
9 12
@@ -62,7 +65,7 @@ private:
62 } 65 }
63 66
64 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); 67 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
65 void UpdateControllers(u64 userdata, int cycles_late); 68 void UpdateControllers(u64 userdata, s64 cycles_late);
66 69
67 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; 70 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
68 71
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 9df7ac50f..d65693fc7 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -319,15 +319,14 @@ public:
319 } 319 }
320 320
321 ASSERT(vm_manager 321 ASSERT(vm_manager
322 .MirrorMemory(*map_address, nro_addr, nro_size, 322 .MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode)
323 Kernel::MemoryState::ModuleCodeStatic)
324 .IsSuccess()); 323 .IsSuccess());
325 ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); 324 ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
326 325
327 if (bss_size > 0) { 326 if (bss_size > 0) {
328 ASSERT(vm_manager 327 ASSERT(vm_manager
329 .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, 328 .MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
330 Kernel::MemoryState::ModuleCodeStatic) 329 Kernel::MemoryState::ModuleCode)
331 .IsSuccess()); 330 .IsSuccess());
332 ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); 331 ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
333 } 332 }
@@ -388,8 +387,7 @@ public:
388 const auto& nro_size = iter->second.size; 387 const auto& nro_size = iter->second.size;
389 388
390 ASSERT(vm_manager 389 ASSERT(vm_manager
391 .MirrorMemory(heap_addr, mapped_addr, nro_size, 390 .MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode)
392 Kernel::MemoryState::ModuleCodeStatic)
393 .IsSuccess()); 391 .IsSuccess());
394 ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess()); 392 ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
395 393
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 1f462e087..2a61593e2 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -42,7 +42,7 @@ private:
42 union { 42 union {
43 BitField<0, 16, Flags> flags; 43 BitField<0, 16, Flags> flags;
44 BitField<16, 8, Severity> severity; 44 BitField<16, 8, Severity> severity;
45 BitField<24, 8, u32_le> verbosity; 45 BitField<24, 8, u32> verbosity;
46 }; 46 };
47 u32_le payload_size; 47 u32_le payload_size;
48 48
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 5c62d42ba..ca88bf97f 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -150,7 +150,7 @@ private:
150 150
151 IPC::ResponseBuilder rb{ctx, 3}; 151 IPC::ResponseBuilder rb{ctx, 3};
152 rb.Push(RESULT_SUCCESS); 152 rb.Push(RESULT_SUCCESS);
153 rb.PushRaw<u8>(Settings::values.enable_nfc); 153 rb.PushRaw<u8>(true);
154 } 154 }
155 155
156 void GetStateOld(Kernel::HLERequestContext& ctx) { 156 void GetStateOld(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 1c4482e47..c6babdd4d 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -335,7 +335,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
335} 335}
336 336
337bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { 337bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
338 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 338 std::lock_guard lock{HLE::g_hle_lock};
339 if (buffer.size() < sizeof(AmiiboFile)) { 339 if (buffer.size() < sizeof(AmiiboFile)) {
340 return false; 340 return false;
341 } 341 }
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 0f02a1a18..4f6042b00 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -19,11 +19,11 @@ public:
19 virtual ~nvdevice() = default; 19 virtual ~nvdevice() = default;
20 union Ioctl { 20 union Ioctl {
21 u32_le raw; 21 u32_le raw;
22 BitField<0, 8, u32_le> cmd; 22 BitField<0, 8, u32> cmd;
23 BitField<8, 8, u32_le> group; 23 BitField<8, 8, u32> group;
24 BitField<16, 14, u32_le> length; 24 BitField<16, 14, u32> length;
25 BitField<30, 1, u32_le> is_in; 25 BitField<30, 1, u32> is_in;
26 BitField<31, 1, u32_le> is_out; 26 BitField<31, 1, u32> is_out;
27 }; 27 };
28 28
29 /** 29 /**
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/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index a34b9e753..af62d33d2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -10,6 +10,7 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 11#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
12#include "core/hle/service/nvdrv/devices/nvmap.h" 12#include "core/hle/service/nvdrv/devices/nvmap.h"
13#include "core/memory.h"
13#include "video_core/memory_manager.h" 14#include "video_core/memory_manager.h"
14#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
15#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
@@ -88,7 +89,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
88 for (const auto& entry : entries) { 89 for (const auto& entry : entries) {
89 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 90 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
90 entry.offset, entry.nvmap_handle, entry.pages); 91 entry.offset, entry.nvmap_handle, entry.pages);
91 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; 92 GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10;
92 auto object = nvmap_dev->GetObject(entry.nvmap_handle); 93 auto object = nvmap_dev->GetObject(entry.nvmap_handle);
93 if (!object) { 94 if (!object) {
94 LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle); 95 LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
@@ -101,7 +102,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
101 u64 size = static_cast<u64>(entry.pages) << 0x10; 102 u64 size = static_cast<u64>(entry.pages) << 0x10;
102 ASSERT(size <= object->size); 103 ASSERT(size <= object->size);
103 104
104 Tegra::GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size); 105 GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size);
105 ASSERT(returned == offset); 106 ASSERT(returned == offset);
106 } 107 }
107 std::memcpy(output.data(), entries.data(), output.size()); 108 std::memcpy(output.data(), entries.data(), output.size());
@@ -172,16 +173,8 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
172 return 0; 173 return 0;
173 } 174 }
174 175
175 auto& system_instance = Core::System::GetInstance(); 176 params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset,
176 177 itr->second.size);
177 // Remove this memory region from the rasterizer cache.
178 auto& gpu = system_instance.GPU();
179 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
180 ASSERT(cpu_addr);
181 gpu.FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
182
183 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
184
185 buffer_mappings.erase(itr->second.offset); 178 buffer_mappings.erase(itr->second.offset);
186 179
187 std::memcpy(output.data(), &params, output.size()); 180 std::memcpy(output.data(), &params, output.size());
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 fc496b654..c7f5bbf28 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -26,7 +26,7 @@
26namespace Service::NVFlinger { 26namespace Service::NVFlinger {
27 27
28constexpr std::size_t SCREEN_REFRESH_RATE = 60; 28constexpr std::size_t SCREEN_REFRESH_RATE = 60;
29constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); 29constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
30 30
31NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { 31NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
32 displays.emplace_back(0, "Default"); 32 displays.emplace_back(0, "Default");
@@ -37,7 +37,7 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
37 37
38 // Schedule the screen composition events 38 // Schedule the screen composition events
39 composition_event = 39 composition_event =
40 core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) { 40 core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) {
41 Compose(); 41 Compose();
42 this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event); 42 this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event);
43 }); 43 });
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_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 c9b4da5b0..ecee554bf 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -2,13 +2,88 @@
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/assert.h"
5#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/file_sys/errors.h"
8#include "core/file_sys/system_archive/system_version.h"
6#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/client_port.h" 10#include "core/hle/kernel/client_port.h"
11#include "core/hle/service/filesystem/filesystem.h"
8#include "core/hle/service/set/set_sys.h" 12#include "core/hle/service/set/set_sys.h"
9 13
10namespace Service::Set { 14namespace Service::Set {
11 15
16namespace {
17constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05;
18
19enum class GetFirmwareVersionType {
20 Version1,
21 Version2,
22};
23
24void GetFirmwareVersionImpl(Kernel::HLERequestContext& ctx, GetFirmwareVersionType type) {
25 LOG_WARNING(Service_SET, "called - Using hardcoded firmware version '{}'",
26 FileSys::SystemArchive::GetLongDisplayVersion());
27
28 ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100,
29 "FirmwareVersion output buffer must be 0x100 bytes in size!");
30
31 // Instead of using the normal procedure of checking for the real system archive and if it
32 // doesn't exist, synthesizing one, I feel that that would lead to strange bugs because a
33 // used is using a really old or really new SystemVersion title. The synthesized one ensures
34 // consistence (currently reports as 5.1.0-0.0)
35 const auto archive = FileSys::SystemArchive::SystemVersion();
36
37 const auto early_exit_failure = [&ctx](const std::string& desc, ResultCode code) {
38 LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).",
39 desc.c_str());
40 IPC::ResponseBuilder rb{ctx, 2};
41 rb.Push(code);
42 };
43
44 if (archive == nullptr) {
45 early_exit_failure("The system version archive couldn't be synthesized.",
46 FileSys::ERROR_FAILED_MOUNT_ARCHIVE);
47 return;
48 }
49
50 const auto ver_file = archive->GetFile("file");
51 if (ver_file == nullptr) {
52 early_exit_failure("The system version archive didn't contain the file 'file'.",
53 FileSys::ERROR_INVALID_ARGUMENT);
54 return;
55 }
56
57 auto data = ver_file->ReadAllBytes();
58 if (data.size() != 0x100) {
59 early_exit_failure("The system version file 'file' was not the correct size.",
60 FileSys::ERROR_OUT_OF_BOUNDS);
61 return;
62 }
63
64 // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will
65 // zero out the REVISION_MINOR field.
66 if (type == GetFirmwareVersionType::Version1) {
67 data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0;
68 }
69
70 ctx.WriteBuffer(data);
71
72 IPC::ResponseBuilder rb{ctx, 2};
73 rb.Push(RESULT_SUCCESS);
74}
75} // Anonymous namespace
76
77void SET_SYS::GetFirmwareVersion(Kernel::HLERequestContext& ctx) {
78 LOG_DEBUG(Service_SET, "called");
79 GetFirmwareVersionImpl(ctx, GetFirmwareVersionType::Version1);
80}
81
82void SET_SYS::GetFirmwareVersion2(Kernel::HLERequestContext& ctx) {
83 LOG_DEBUG(Service_SET, "called");
84 GetFirmwareVersionImpl(ctx, GetFirmwareVersionType::Version2);
85}
86
12void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) { 87void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) {
13 LOG_DEBUG(Service_SET, "called"); 88 LOG_DEBUG(Service_SET, "called");
14 89
@@ -33,8 +108,8 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
33 {0, nullptr, "SetLanguageCode"}, 108 {0, nullptr, "SetLanguageCode"},
34 {1, nullptr, "SetNetworkSettings"}, 109 {1, nullptr, "SetNetworkSettings"},
35 {2, nullptr, "GetNetworkSettings"}, 110 {2, nullptr, "GetNetworkSettings"},
36 {3, nullptr, "GetFirmwareVersion"}, 111 {3, &SET_SYS::GetFirmwareVersion, "GetFirmwareVersion"},
37 {4, nullptr, "GetFirmwareVersion2"}, 112 {4, &SET_SYS::GetFirmwareVersion2, "GetFirmwareVersion2"},
38 {5, nullptr, "GetFirmwareVersionDigest"}, 113 {5, nullptr, "GetFirmwareVersionDigest"},
39 {7, nullptr, "GetLockScreenFlag"}, 114 {7, nullptr, "GetLockScreenFlag"},
40 {8, nullptr, "SetLockScreenFlag"}, 115 {8, nullptr, "SetLockScreenFlag"},
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index f602f3c77..13ee2cf46 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -20,6 +20,8 @@ private:
20 BasicBlack = 1, 20 BasicBlack = 1,
21 }; 21 };
22 22
23 void GetFirmwareVersion(Kernel::HLERequestContext& ctx);
24 void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);
23 void GetColorSetId(Kernel::HLERequestContext& ctx); 25 void GetColorSetId(Kernel::HLERequestContext& ctx);
24 void SetColorSetId(Kernel::HLERequestContext& ctx); 26 void SetColorSetId(Kernel::HLERequestContext& ctx);
25 27
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..f7f87a958 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);
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/elf.cpp b/src/core/loader/elf.cpp
index 6057c7f26..46ac372f6 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -9,6 +9,7 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/hle/kernel/code_set.h"
12#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/vm_manager.h" 14#include "core/hle/kernel/vm_manager.h"
14#include "core/loader/elf.h" 15#include "core/loader/elf.h"
@@ -340,7 +341,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
340 } 341 }
341 342
342 codeset.entrypoint = base_addr + header->e_entry; 343 codeset.entrypoint = base_addr + header->e_entry;
343 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 344 codeset.memory = std::move(program_image);
344 345
345 LOG_DEBUG(Loader, "Done loading."); 346 LOG_DEBUG(Loader, "Done loading.");
346 347
diff --git a/src/core/loader/linker.cpp b/src/core/loader/linker.cpp
deleted file mode 100644
index 57ca8c3ee..000000000
--- a/src/core/loader/linker.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <vector>
6
7#include "common/common_funcs.h"
8#include "common/logging/log.h"
9#include "common/swap.h"
10#include "core/loader/linker.h"
11#include "core/memory.h"
12
13namespace Loader {
14
15enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 };
16
17enum DynamicType : u32 {
18 DT_NULL = 0,
19 DT_PLTRELSZ = 2,
20 DT_STRTAB = 5,
21 DT_SYMTAB = 6,
22 DT_RELA = 7,
23 DT_RELASZ = 8,
24 DT_STRSZ = 10,
25 DT_JMPREL = 23,
26};
27
28struct Elf64_Rela {
29 u64_le offset;
30 RelocationType type;
31 u32_le symbol;
32 s64_le addend;
33};
34static_assert(sizeof(Elf64_Rela) == 0x18, "Elf64_Rela has incorrect size.");
35
36struct Elf64_Dyn {
37 u64_le tag;
38 u64_le value;
39};
40static_assert(sizeof(Elf64_Dyn) == 0x10, "Elf64_Dyn has incorrect size.");
41
42struct Elf64_Sym {
43 u32_le name;
44 INSERT_PADDING_BYTES(0x2);
45 u16_le shndx;
46 u64_le value;
47 u64_le size;
48};
49static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size.");
50
51void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
52 u64 relocation_offset, u64 size, VAddr load_base) {
53 for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) {
54 Elf64_Rela rela;
55 std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela));
56
57 const Symbol& symbol = symbols[rela.symbol];
58 switch (rela.type) {
59 case RelocationType::RELATIVE: {
60 const u64 value = load_base + rela.addend;
61 if (!symbol.name.empty()) {
62 exports[symbol.name] = value;
63 }
64 std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
65 break;
66 }
67 case RelocationType::JUMP_SLOT:
68 case RelocationType::GLOB_DAT:
69 if (!symbol.value) {
70 imports[symbol.name] = {rela.offset + load_base, 0};
71 } else {
72 exports[symbol.name] = symbol.value;
73 std::memcpy(&program_image[rela.offset], &symbol.value, sizeof(u64));
74 }
75 break;
76 case RelocationType::ABS64:
77 if (!symbol.value) {
78 imports[symbol.name] = {rela.offset + load_base, rela.addend};
79 } else {
80 const u64 value = symbol.value + rela.addend;
81 exports[symbol.name] = value;
82 std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
83 }
84 break;
85 default:
86 LOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type));
87 break;
88 }
89 }
90}
91
92void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base) {
93 std::map<u64, u64> dynamic;
94 while (dynamic_section_offset < program_image.size()) {
95 Elf64_Dyn dyn;
96 std::memcpy(&dyn, &program_image[dynamic_section_offset], sizeof(Elf64_Dyn));
97 dynamic_section_offset += sizeof(Elf64_Dyn);
98
99 if (dyn.tag == DT_NULL) {
100 break;
101 }
102 dynamic[dyn.tag] = dyn.value;
103 }
104
105 u64 offset = dynamic[DT_SYMTAB];
106 std::vector<Symbol> symbols;
107 while (offset < program_image.size()) {
108 Elf64_Sym sym;
109 std::memcpy(&sym, &program_image[offset], sizeof(Elf64_Sym));
110 offset += sizeof(Elf64_Sym);
111
112 if (sym.name >= dynamic[DT_STRSZ]) {
113 break;
114 }
115
116 std::string name = reinterpret_cast<char*>(&program_image[dynamic[DT_STRTAB] + sym.name]);
117 if (sym.value) {
118 exports[name] = load_base + sym.value;
119 symbols.emplace_back(std::move(name), load_base + sym.value);
120 } else {
121 symbols.emplace_back(std::move(name), 0);
122 }
123 }
124
125 if (dynamic.find(DT_RELA) != dynamic.end()) {
126 WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], load_base);
127 }
128
129 if (dynamic.find(DT_JMPREL) != dynamic.end()) {
130 WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ],
131 load_base);
132 }
133}
134
135void Linker::ResolveImports() {
136 // Resolve imports
137 for (const auto& import : imports) {
138 const auto& search = exports.find(import.first);
139 if (search != exports.end()) {
140 Memory::Write64(import.second.ea, search->second + import.second.addend);
141 } else {
142 LOG_ERROR(Loader, "Unresolved import: {}", import.first);
143 }
144 }
145}
146
147} // namespace Loader
diff --git a/src/core/loader/linker.h b/src/core/loader/linker.h
deleted file mode 100644
index 107625837..000000000
--- a/src/core/loader/linker.h
+++ /dev/null
@@ -1,36 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <string>
9#include "common/common_types.h"
10
11namespace Loader {
12
13class Linker {
14protected:
15 struct Symbol {
16 Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {}
17 std::string name;
18 u64 value;
19 };
20
21 struct Import {
22 VAddr ea;
23 s64 addend;
24 };
25
26 void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
27 u64 relocation_offset, u64 size, VAddr load_base);
28 void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base);
29
30 void ResolveImports();
31
32 std::map<std::string, Import> imports;
33 std::map<std::string, VAddr> exports;
34};
35
36} // namespace Loader
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 4fad0c0dd..31e4a0c84 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -14,6 +14,7 @@
14#include "core/file_sys/romfs_factory.h" 14#include "core/file_sys/romfs_factory.h"
15#include "core/file_sys/vfs_offset.h" 15#include "core/file_sys/vfs_offset.h"
16#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/code_set.h"
17#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
18#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
19#include "core/hle/service/filesystem/filesystem.h" 20#include "core/hle/service/filesystem/filesystem.h"
@@ -186,7 +187,7 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
186 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 187 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
187 188
188 // Load codeset for current process 189 // Load codeset for current process
189 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 190 codeset.memory = std::move(program_image);
190 process.LoadModule(std::move(codeset), load_base); 191 process.LoadModule(std::move(codeset), load_base);
191 192
192 // Register module with GDBStub 193 // Register module with GDBStub
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 013d629c0..85b0ed644 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -4,10 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
7#include <string> 8#include <string>
8#include <vector> 9#include <vector>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/loader/linker.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
13namespace FileSys { 13namespace FileSys {
@@ -21,7 +21,7 @@ class Process;
21namespace Loader { 21namespace Loader {
22 22
23/// Loads an NRO file 23/// Loads an NRO file
24class AppLoader_NRO final : public AppLoader, Linker { 24class AppLoader_NRO final : public AppLoader {
25public: 25public:
26 explicit AppLoader_NRO(FileSys::VirtualFile file); 26 explicit AppLoader_NRO(FileSys::VirtualFile file);
27 ~AppLoader_NRO() override; 27 ~AppLoader_NRO() override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 6ded0b707..ffe2eea8a 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -4,13 +4,17 @@
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/logging/log.h" 11#include "common/logging/log.h"
12#include "common/lz4_compression.h"
11#include "common/swap.h" 13#include "common/swap.h"
14#include "core/core.h"
12#include "core/file_sys/patch_manager.h" 15#include "core/file_sys/patch_manager.h"
13#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/code_set.h"
14#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
16#include "core/loader/nso.h" 20#include "core/loader/nso.h"
@@ -18,36 +22,8 @@
18#include "core/settings.h" 22#include "core/settings.h"
19 23
20namespace Loader { 24namespace Loader {
21 25namespace {
22struct NsoSegmentHeader { 26struct MODHeader {
23 u32_le offset;
24 u32_le location;
25 u32_le size;
26 union {
27 u32_le alignment;
28 u32_le bss_size;
29 };
30};
31static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
32
33struct NsoHeader {
34 u32_le magic;
35 u32_le version;
36 INSERT_PADDING_WORDS(1);
37 u8 flags;
38 std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
39 std::array<u8, 0x20> build_id;
40 std::array<u32_le, 3> segments_compressed_size;
41
42 bool IsSegmentCompressed(size_t segment_num) const {
43 ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
44 return ((flags >> segment_num) & 1);
45 }
46};
47static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
48static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
49
50struct ModHeader {
51 u32_le magic; 27 u32_le magic;
52 u32_le dynamic_offset; 28 u32_le dynamic_offset;
53 u32_le bss_start_offset; 29 u32_le bss_start_offset;
@@ -56,7 +32,28 @@ struct ModHeader {
56 u32_le eh_frame_hdr_end_offset; 32 u32_le eh_frame_hdr_end_offset;
57 u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base 33 u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
58}; 34};
59static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); 35static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
36
37std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
38 const NSOSegmentHeader& header) {
39 const std::vector<u8> uncompressed_data =
40 Common::Compression::DecompressDataLZ4(compressed_data, header.size);
41
42 ASSERT_MSG(uncompressed_data.size() == static_cast<int>(header.size), "{} != {}", header.size,
43 uncompressed_data.size());
44
45 return uncompressed_data;
46}
47
48constexpr u32 PageAlignSize(u32 size) {
49 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
50}
51} // Anonymous namespace
52
53bool NSOHeader::IsSegmentCompressed(size_t segment_num) const {
54 ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
55 return ((flags >> segment_num) & 1) != 0;
56}
60 57
61AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} 58AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
62 59
@@ -73,38 +70,22 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
73 return FileType::NSO; 70 return FileType::NSO;
74} 71}
75 72
76static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
77 const NsoSegmentHeader& header) {
78 std::vector<u8> uncompressed_data(header.size);
79 const int bytes_uncompressed =
80 LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
81 reinterpret_cast<char*>(uncompressed_data.data()),
82 static_cast<int>(compressed_data.size()), header.size);
83
84 ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
85 bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
86 "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
87
88 return uncompressed_data;
89}
90
91static constexpr u32 PageAlignSize(u32 size) {
92 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
93}
94
95std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, 73std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
96 const FileSys::VfsFile& file, VAddr load_base, 74 const FileSys::VfsFile& file, VAddr load_base,
97 bool should_pass_arguments, 75 bool should_pass_arguments,
98 std::optional<FileSys::PatchManager> pm) { 76 std::optional<FileSys::PatchManager> pm) {
99 if (file.GetSize() < sizeof(NsoHeader)) 77 if (file.GetSize() < sizeof(NSOHeader)) {
100 return {}; 78 return {};
79 }
101 80
102 NsoHeader nso_header{}; 81 NSOHeader nso_header{};
103 if (sizeof(NsoHeader) != file.ReadObject(&nso_header)) 82 if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
104 return {}; 83 return {};
84 }
105 85
106 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 86 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
107 return {}; 87 return {};
88 }
108 89
109 // Build program image 90 // Build program image
110 Kernel::CodeSet codeset; 91 Kernel::CodeSet codeset;
@@ -140,10 +121,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
140 std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); 121 std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
141 122
142 // Read MOD header 123 // Read MOD header
143 ModHeader mod_header{}; 124 MODHeader mod_header{};
144 // Default .bss to size in segment header if MOD0 section doesn't exist 125 // Default .bss to size in segment header if MOD0 section doesn't exist
145 u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; 126 u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
146 std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); 127 std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader));
147 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; 128 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
148 if (has_mod_header) { 129 if (has_mod_header) {
149 // Resize program image to include .bss section and page align each section 130 // Resize program image to include .bss section and page align each section
@@ -155,17 +136,29 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
155 136
156 // Apply patches if necessary 137 // Apply patches if necessary
157 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { 138 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
158 std::vector<u8> pi_header(program_image.size() + 0x100); 139 std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 140 pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 141 reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
142 pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
143 program_image.end());
161 144
162 pi_header = pm->PatchNSO(pi_header); 145 pi_header = pm->PatchNSO(pi_header);
163 146
164 std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size()); 147 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
148 }
149
150 // Apply cheats if they exist and the program has a valid title ID
151 if (pm) {
152 auto& system = Core::System::GetInstance();
153 const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
154 if (!cheats.empty()) {
155 system.RegisterCheatList(cheats, Common::HexArrayToString(nso_header.build_id),
156 load_base, load_base + program_image.size());
157 }
165 } 158 }
166 159
167 // Load codeset for current process 160 // Load codeset for current process
168 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 161 codeset.memory = std::move(program_image);
169 process.LoadModule(std::move(codeset), load_base); 162 process.LoadModule(std::move(codeset), load_base);
170 163
171 // Register module with GDBStub 164 // Register module with GDBStub
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 135b6ea5a..4674c3724 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,10 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <optional> 8#include <optional>
9#include <type_traits>
8#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h"
9#include "core/file_sys/patch_manager.h" 12#include "core/file_sys/patch_manager.h"
10#include "core/loader/linker.h"
11#include "core/loader/loader.h" 13#include "core/loader/loader.h"
12 14
13namespace Kernel { 15namespace Kernel {
@@ -16,6 +18,43 @@ class Process;
16 18
17namespace Loader { 19namespace Loader {
18 20
21struct NSOSegmentHeader {
22 u32_le offset;
23 u32_le location;
24 u32_le size;
25 union {
26 u32_le alignment;
27 u32_le bss_size;
28 };
29};
30static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
31
32struct NSOHeader {
33 using SHA256Hash = std::array<u8, 0x20>;
34
35 struct RODataRelativeExtent {
36 u32_le data_offset;
37 u32_le size;
38 };
39
40 u32_le magic;
41 u32_le version;
42 u32 reserved;
43 u32_le flags;
44 std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
45 std::array<u8, 0x20> build_id;
46 std::array<u32_le, 3> segments_compressed_size;
47 std::array<u8, 0x1C> padding;
48 RODataRelativeExtent api_info_extent;
49 RODataRelativeExtent dynstr_extent;
50 RODataRelativeExtent dynsyn_extent;
51 std::array<SHA256Hash, 3> segment_hashes;
52
53 bool IsSegmentCompressed(size_t segment_num) const;
54};
55static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
56static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
57
19constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; 58constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
20 59
21struct NSOArgumentHeader { 60struct NSOArgumentHeader {
@@ -26,7 +65,7 @@ struct NSOArgumentHeader {
26static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size."); 65static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
27 66
28/// Loads an NSO file 67/// Loads an NSO file
29class AppLoader_NSO final : public AppLoader, Linker { 68class AppLoader_NSO final : public AppLoader {
30public: 69public:
31 explicit AppLoader_NSO(FileSys::VirtualFile file); 70 explicit AppLoader_NSO(FileSys::VirtualFile file);
32 71
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 6591c45d2..332c1037c 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.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 "common/page_table.h"
13#include "common/swap.h" 14#include "common/swap.h"
14#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
15#include "core/core.h" 16#include "core/core.h"
@@ -18,13 +19,14 @@
18#include "core/hle/lock.h" 19#include "core/hle/lock.h"
19#include "core/memory.h" 20#include "core/memory.h"
20#include "core/memory_setup.h" 21#include "core/memory_setup.h"
22#include "video_core/gpu.h"
21#include "video_core/renderer_base.h" 23#include "video_core/renderer_base.h"
22 24
23namespace Memory { 25namespace Memory {
24 26
25static PageTable* current_page_table = nullptr; 27static Common::PageTable* current_page_table = nullptr;
26 28
27void SetCurrentPageTable(PageTable* page_table) { 29void SetCurrentPageTable(Common::PageTable* page_table) {
28 current_page_table = page_table; 30 current_page_table = page_table;
29 31
30 auto& system = Core::System::GetInstance(); 32 auto& system = Core::System::GetInstance();
@@ -36,39 +38,20 @@ void SetCurrentPageTable(PageTable* page_table) {
36 } 38 }
37} 39}
38 40
39PageTable* GetCurrentPageTable() { 41Common::PageTable* GetCurrentPageTable() {
40 return current_page_table; 42 return current_page_table;
41} 43}
42 44
43PageTable::PageTable() = default; 45static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
44 46 Common::PageType type) {
45PageTable::PageTable(std::size_t address_space_width_in_bits) {
46 Resize(address_space_width_in_bits);
47}
48
49PageTable::~PageTable() = default;
50
51void PageTable::Resize(std::size_t address_space_width_in_bits) {
52 const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS);
53
54 pointers.resize(num_page_table_entries);
55 attributes.resize(num_page_table_entries);
56
57 // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
58 // vector size is subsequently decreased (via resize), the vector might not automatically
59 // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
60 // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
61
62 pointers.shrink_to_fit();
63 attributes.shrink_to_fit();
64}
65
66static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
67 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, 47 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
68 (base + size) * PAGE_SIZE); 48 (base + size) * PAGE_SIZE);
69 49
70 RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, 50 // During boot, current_page_table might not be set yet, in which case we need not flush
71 FlushMode::FlushAndInvalidate); 51 if (Core::System::GetInstance().IsPoweredOn()) {
52 Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS,
53 size * PAGE_SIZE);
54 }
72 55
73 VAddr end = base + size; 56 VAddr end = base + size;
74 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", 57 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
@@ -88,41 +71,47 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
88 } 71 }
89} 72}
90 73
91void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target) { 74void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
92 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 75 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
93 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 76 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
94 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); 77 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
95} 78}
96 79
97void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler) { 80void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
81 Common::MemoryHookPointer mmio_handler) {
98 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 82 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
99 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 83 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
100 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); 84 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Special);
101 85
102 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 86 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
103 SpecialRegion region{SpecialRegion::Type::IODevice, std::move(mmio_handler)}; 87 Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)};
104 page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region})); 88 page_table.special_regions.add(
89 std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
105} 90}
106 91
107void UnmapRegion(PageTable& page_table, VAddr base, u64 size) { 92void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
108 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 93 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
109 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 94 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
110 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); 95 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Unmapped);
111 96
112 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 97 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
113 page_table.special_regions.erase(interval); 98 page_table.special_regions.erase(interval);
114} 99}
115 100
116void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) { 101void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
102 Common::MemoryHookPointer hook) {
117 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 103 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
118 SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)}; 104 Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
119 page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region})); 105 page_table.special_regions.add(
106 std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
120} 107}
121 108
122void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) { 109void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
110 Common::MemoryHookPointer hook) {
123 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 111 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
124 SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)}; 112 Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
125 page_table.special_regions.subtract(std::make_pair(interval, std::set<SpecialRegion>{region})); 113 page_table.special_regions.subtract(
114 std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
126} 115}
127 116
128/** 117/**
@@ -171,19 +160,19 @@ T Read(const VAddr vaddr) {
171 return value; 160 return value;
172 } 161 }
173 162
174 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 163 Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
175 switch (type) { 164 switch (type) {
176 case PageType::Unmapped: 165 case Common::PageType::Unmapped:
177 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); 166 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
178 return 0; 167 return 0;
179 case PageType::Memory: 168 case Common::PageType::Memory:
180 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 169 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
181 break; 170 break;
182 case PageType::RasterizerCachedMemory: { 171 case Common::PageType::RasterizerCachedMemory: {
183 RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Flush); 172 auto host_ptr{GetPointerFromVMA(vaddr)};
184 173 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
185 T value; 174 T value;
186 std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T)); 175 std::memcpy(&value, host_ptr, sizeof(T));
187 return value; 176 return value;
188 } 177 }
189 default: 178 default:
@@ -201,18 +190,19 @@ void Write(const VAddr vaddr, const T data) {
201 return; 190 return;
202 } 191 }
203 192
204 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 193 Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
205 switch (type) { 194 switch (type) {
206 case PageType::Unmapped: 195 case Common::PageType::Unmapped:
207 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 196 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
208 static_cast<u32>(data), vaddr); 197 static_cast<u32>(data), vaddr);
209 return; 198 return;
210 case PageType::Memory: 199 case Common::PageType::Memory:
211 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 200 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
212 break; 201 break;
213 case PageType::RasterizerCachedMemory: { 202 case Common::PageType::RasterizerCachedMemory: {
214 RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Invalidate); 203 auto host_ptr{GetPointerFromVMA(vaddr)};
215 std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T)); 204 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
205 std::memcpy(host_ptr, &data, sizeof(T));
216 break; 206 break;
217 } 207 }
218 default: 208 default:
@@ -227,10 +217,10 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
227 if (page_pointer) 217 if (page_pointer)
228 return true; 218 return true;
229 219
230 if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) 220 if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory)
231 return true; 221 return true;
232 222
233 if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special) 223 if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special)
234 return false; 224 return false;
235 225
236 return false; 226 return false;
@@ -250,7 +240,8 @@ u8* GetPointer(const VAddr vaddr) {
250 return page_pointer + (vaddr & PAGE_MASK); 240 return page_pointer + (vaddr & PAGE_MASK);
251 } 241 }
252 242
253 if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) { 243 if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
244 Common::PageType::RasterizerCachedMemory) {
254 return GetPointerFromVMA(vaddr); 245 return GetPointerFromVMA(vaddr);
255 } 246 }
256 247
@@ -284,20 +275,20 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
284 275
285 u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; 276 u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
286 for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { 277 for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
287 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; 278 Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
288 279
289 if (cached) { 280 if (cached) {
290 // Switch page type to cached if now cached 281 // Switch page type to cached if now cached
291 switch (page_type) { 282 switch (page_type) {
292 case PageType::Unmapped: 283 case Common::PageType::Unmapped:
293 // It is not necessary for a process to have this region mapped into its address 284 // It is not necessary for a process to have this region mapped into its address
294 // space, for example, a system module need not have a VRAM mapping. 285 // space, for example, a system module need not have a VRAM mapping.
295 break; 286 break;
296 case PageType::Memory: 287 case Common::PageType::Memory:
297 page_type = PageType::RasterizerCachedMemory; 288 page_type = Common::PageType::RasterizerCachedMemory;
298 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; 289 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
299 break; 290 break;
300 case PageType::RasterizerCachedMemory: 291 case Common::PageType::RasterizerCachedMemory:
301 // There can be more than one GPU region mapped per CPU region, so it's common that 292 // There can be more than one GPU region mapped per CPU region, so it's common that
302 // this area is already marked as cached. 293 // this area is already marked as cached.
303 break; 294 break;
@@ -307,23 +298,23 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
307 } else { 298 } else {
308 // Switch page type to uncached if now uncached 299 // Switch page type to uncached if now uncached
309 switch (page_type) { 300 switch (page_type) {
310 case PageType::Unmapped: 301 case Common::PageType::Unmapped:
311 // It is not necessary for a process to have this region mapped into its address 302 // It is not necessary for a process to have this region mapped into its address
312 // space, for example, a system module need not have a VRAM mapping. 303 // space, for example, a system module need not have a VRAM mapping.
313 break; 304 break;
314 case PageType::Memory: 305 case Common::PageType::Memory:
315 // There can be more than one GPU region mapped per CPU region, so it's common that 306 // There can be more than one GPU region mapped per CPU region, so it's common that
316 // this area is already unmarked as cached. 307 // this area is already unmarked as cached.
317 break; 308 break;
318 case PageType::RasterizerCachedMemory: { 309 case Common::PageType::RasterizerCachedMemory: {
319 u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); 310 u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
320 if (pointer == nullptr) { 311 if (pointer == nullptr) {
321 // It's possible that this function has been called while updating the pagetable 312 // It's possible that this function has been called while updating the pagetable
322 // after unmapping a VMA. In that case the underlying VMA will no longer exist, 313 // after unmapping a VMA. In that case the underlying VMA will no longer exist,
323 // and we should just leave the pagetable entry blank. 314 // and we should just leave the pagetable entry blank.
324 page_type = PageType::Unmapped; 315 page_type = Common::PageType::Unmapped;
325 } else { 316 } else {
326 page_type = PageType::Memory; 317 page_type = Common::PageType::Memory;
327 current_page_table->pointers[vaddr >> PAGE_BITS] = pointer; 318 current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
328 } 319 }
329 break; 320 break;
@@ -335,47 +326,6 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
335 } 326 }
336} 327}
337 328
338void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
339 auto& system_instance = Core::System::GetInstance();
340
341 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
342 // null here
343 if (!system_instance.IsPoweredOn()) {
344 return;
345 }
346
347 const VAddr end = start + size;
348
349 const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
350 if (start >= region_end || end <= region_start) {
351 // No overlap with region
352 return;
353 }
354
355 const VAddr overlap_start = std::max(start, region_start);
356 const VAddr overlap_end = std::min(end, region_end);
357 const VAddr overlap_size = overlap_end - overlap_start;
358
359 auto& gpu = system_instance.GPU();
360 switch (mode) {
361 case FlushMode::Flush:
362 gpu.FlushRegion(overlap_start, overlap_size);
363 break;
364 case FlushMode::Invalidate:
365 gpu.InvalidateRegion(overlap_start, overlap_size);
366 break;
367 case FlushMode::FlushAndInvalidate:
368 gpu.FlushAndInvalidateRegion(overlap_start, overlap_size);
369 break;
370 }
371 };
372
373 const auto& vm_manager = Core::CurrentProcess()->VMManager();
374
375 CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress());
376 CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress());
377}
378
379u8 Read8(const VAddr addr) { 329u8 Read8(const VAddr addr) {
380 return Read<u8>(addr); 330 return Read<u8>(addr);
381} 331}
@@ -406,24 +356,24 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
406 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 356 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
407 357
408 switch (page_table.attributes[page_index]) { 358 switch (page_table.attributes[page_index]) {
409 case PageType::Unmapped: { 359 case Common::PageType::Unmapped: {
410 LOG_ERROR(HW_Memory, 360 LOG_ERROR(HW_Memory,
411 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 361 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
412 current_vaddr, src_addr, size); 362 current_vaddr, src_addr, size);
413 std::memset(dest_buffer, 0, copy_amount); 363 std::memset(dest_buffer, 0, copy_amount);
414 break; 364 break;
415 } 365 }
416 case PageType::Memory: { 366 case Common::PageType::Memory: {
417 DEBUG_ASSERT(page_table.pointers[page_index]); 367 DEBUG_ASSERT(page_table.pointers[page_index]);
418 368
419 const u8* src_ptr = page_table.pointers[page_index] + page_offset; 369 const u8* src_ptr = page_table.pointers[page_index] + page_offset;
420 std::memcpy(dest_buffer, src_ptr, copy_amount); 370 std::memcpy(dest_buffer, src_ptr, copy_amount);
421 break; 371 break;
422 } 372 }
423 case PageType::RasterizerCachedMemory: { 373 case Common::PageType::RasterizerCachedMemory: {
424 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount), 374 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
425 FlushMode::Flush); 375 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
426 std::memcpy(dest_buffer, GetPointerFromVMA(process, current_vaddr), copy_amount); 376 std::memcpy(dest_buffer, host_ptr, copy_amount);
427 break; 377 break;
428 } 378 }
429 default: 379 default:
@@ -470,23 +420,23 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
470 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 420 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
471 421
472 switch (page_table.attributes[page_index]) { 422 switch (page_table.attributes[page_index]) {
473 case PageType::Unmapped: { 423 case Common::PageType::Unmapped: {
474 LOG_ERROR(HW_Memory, 424 LOG_ERROR(HW_Memory,
475 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 425 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
476 current_vaddr, dest_addr, size); 426 current_vaddr, dest_addr, size);
477 break; 427 break;
478 } 428 }
479 case PageType::Memory: { 429 case Common::PageType::Memory: {
480 DEBUG_ASSERT(page_table.pointers[page_index]); 430 DEBUG_ASSERT(page_table.pointers[page_index]);
481 431
482 u8* dest_ptr = page_table.pointers[page_index] + page_offset; 432 u8* dest_ptr = page_table.pointers[page_index] + page_offset;
483 std::memcpy(dest_ptr, src_buffer, copy_amount); 433 std::memcpy(dest_ptr, src_buffer, copy_amount);
484 break; 434 break;
485 } 435 }
486 case PageType::RasterizerCachedMemory: { 436 case Common::PageType::RasterizerCachedMemory: {
487 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount), 437 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
488 FlushMode::Invalidate); 438 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
489 std::memcpy(GetPointerFromVMA(process, current_vaddr), src_buffer, copy_amount); 439 std::memcpy(host_ptr, src_buffer, copy_amount);
490 break; 440 break;
491 } 441 }
492 default: 442 default:
@@ -516,23 +466,23 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std:
516 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 466 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
517 467
518 switch (page_table.attributes[page_index]) { 468 switch (page_table.attributes[page_index]) {
519 case PageType::Unmapped: { 469 case Common::PageType::Unmapped: {
520 LOG_ERROR(HW_Memory, 470 LOG_ERROR(HW_Memory,
521 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 471 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
522 current_vaddr, dest_addr, size); 472 current_vaddr, dest_addr, size);
523 break; 473 break;
524 } 474 }
525 case PageType::Memory: { 475 case Common::PageType::Memory: {
526 DEBUG_ASSERT(page_table.pointers[page_index]); 476 DEBUG_ASSERT(page_table.pointers[page_index]);
527 477
528 u8* dest_ptr = page_table.pointers[page_index] + page_offset; 478 u8* dest_ptr = page_table.pointers[page_index] + page_offset;
529 std::memset(dest_ptr, 0, copy_amount); 479 std::memset(dest_ptr, 0, copy_amount);
530 break; 480 break;
531 } 481 }
532 case PageType::RasterizerCachedMemory: { 482 case Common::PageType::RasterizerCachedMemory: {
533 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount), 483 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
534 FlushMode::Invalidate); 484 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
535 std::memset(GetPointerFromVMA(process, current_vaddr), 0, copy_amount); 485 std::memset(host_ptr, 0, copy_amount);
536 break; 486 break;
537 } 487 }
538 default: 488 default:
@@ -558,23 +508,23 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
558 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 508 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
559 509
560 switch (page_table.attributes[page_index]) { 510 switch (page_table.attributes[page_index]) {
561 case PageType::Unmapped: { 511 case Common::PageType::Unmapped: {
562 LOG_ERROR(HW_Memory, 512 LOG_ERROR(HW_Memory,
563 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 513 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
564 current_vaddr, src_addr, size); 514 current_vaddr, src_addr, size);
565 ZeroBlock(process, dest_addr, copy_amount); 515 ZeroBlock(process, dest_addr, copy_amount);
566 break; 516 break;
567 } 517 }
568 case PageType::Memory: { 518 case Common::PageType::Memory: {
569 DEBUG_ASSERT(page_table.pointers[page_index]); 519 DEBUG_ASSERT(page_table.pointers[page_index]);
570 const u8* src_ptr = page_table.pointers[page_index] + page_offset; 520 const u8* src_ptr = page_table.pointers[page_index] + page_offset;
571 WriteBlock(process, dest_addr, src_ptr, copy_amount); 521 WriteBlock(process, dest_addr, src_ptr, copy_amount);
572 break; 522 break;
573 } 523 }
574 case PageType::RasterizerCachedMemory: { 524 case Common::PageType::RasterizerCachedMemory: {
575 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount), 525 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
576 FlushMode::Flush); 526 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
577 WriteBlock(process, dest_addr, GetPointerFromVMA(process, current_vaddr), copy_amount); 527 WriteBlock(process, dest_addr, host_ptr, copy_amount);
578 break; 528 break;
579 } 529 }
580 default: 530 default:
diff --git a/src/core/memory.h b/src/core/memory.h
index 1acf5ce8c..1d38cdca8 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -6,11 +6,11 @@
6 6
7#include <cstddef> 7#include <cstddef>
8#include <string> 8#include <string>
9#include <tuple>
10#include <vector>
11#include <boost/icl/interval_map.hpp>
12#include "common/common_types.h" 9#include "common/common_types.h"
13#include "core/memory_hook.h" 10
11namespace Common {
12struct PageTable;
13}
14 14
15namespace Kernel { 15namespace Kernel {
16class Process; 16class Process;
@@ -26,71 +26,6 @@ constexpr std::size_t PAGE_BITS = 12;
26constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS; 26constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
27constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 27constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
28 28
29enum class PageType : u8 {
30 /// Page is unmapped and should cause an access error.
31 Unmapped,
32 /// Page is mapped to regular memory. This is the only type you can get pointers to.
33 Memory,
34 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
35 /// invalidation
36 RasterizerCachedMemory,
37 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
38 Special,
39};
40
41struct SpecialRegion {
42 enum class Type {
43 DebugHook,
44 IODevice,
45 } type;
46
47 MemoryHookPointer handler;
48
49 bool operator<(const SpecialRegion& other) const {
50 return std::tie(type, handler) < std::tie(other.type, other.handler);
51 }
52
53 bool operator==(const SpecialRegion& other) const {
54 return std::tie(type, handler) == std::tie(other.type, other.handler);
55 }
56};
57
58/**
59 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
60 * mimics the way a real CPU page table works.
61 */
62struct PageTable {
63 explicit PageTable();
64 explicit PageTable(std::size_t address_space_width_in_bits);
65 ~PageTable();
66
67 /**
68 * Resizes the page table to be able to accomodate enough pages within
69 * a given address space.
70 *
71 * @param address_space_width_in_bits The address size width in bits.
72 */
73 void Resize(std::size_t address_space_width_in_bits);
74
75 /**
76 * Vector of memory pointers backing each page. An entry can only be non-null if the
77 * corresponding entry in the `attributes` vector is of type `Memory`.
78 */
79 std::vector<u8*> pointers;
80
81 /**
82 * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
83 * of type `Special`.
84 */
85 boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
86
87 /**
88 * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
89 * the corresponding entry in `pointers` MUST be set to null.
90 */
91 std::vector<PageType> attributes;
92};
93
94/// Virtual user-space memory regions 29/// Virtual user-space memory regions
95enum : VAddr { 30enum : VAddr {
96 /// Read-only page containing kernel and system configuration values. 31 /// Read-only page containing kernel and system configuration values.
@@ -116,8 +51,8 @@ enum : VAddr {
116}; 51};
117 52
118/// Currently active page table 53/// Currently active page table
119void SetCurrentPageTable(PageTable* page_table); 54void SetCurrentPageTable(Common::PageTable* page_table);
120PageTable* GetCurrentPageTable(); 55Common::PageTable* GetCurrentPageTable();
121 56
122/// Determines if the given VAddr is valid for the specified process. 57/// Determines if the given VAddr is valid for the specified process.
123bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); 58bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
@@ -161,10 +96,4 @@ enum class FlushMode {
161 */ 96 */
162void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached); 97void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
163 98
164/**
165 * Flushes and invalidates any externally cached rasterizer resources touching the given virtual
166 * address region.
167 */
168void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode);
169
170} // namespace Memory 99} // namespace Memory
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
index 9a1a4f4be..5225ee8e2 100644
--- a/src/core/memory_setup.h
+++ b/src/core/memory_setup.h
@@ -5,7 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/memory_hook.h" 8#include "common/memory_hook.h"
9
10namespace Common {
11struct PageTable;
12}
9 13
10namespace Memory { 14namespace Memory {
11 15
@@ -17,7 +21,7 @@ namespace Memory {
17 * @param size The amount of bytes to map. Must be page-aligned. 21 * @param size The amount of bytes to map. Must be page-aligned.
18 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. 22 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
19 */ 23 */
20void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target); 24void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
21 25
22/** 26/**
23 * Maps a region of the emulated process address space as a IO region. 27 * Maps a region of the emulated process address space as a IO region.
@@ -26,11 +30,14 @@ void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target);
26 * @param size The amount of bytes to map. Must be page-aligned. 30 * @param size The amount of bytes to map. Must be page-aligned.
27 * @param mmio_handler The handler that backs the mapping. 31 * @param mmio_handler The handler that backs the mapping.
28 */ 32 */
29void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler); 33void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
34 Common::MemoryHookPointer mmio_handler);
30 35
31void UnmapRegion(PageTable& page_table, VAddr base, u64 size); 36void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
32 37
33void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook); 38void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
34void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook); 39 Common::MemoryHookPointer hook);
40void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
41 Common::MemoryHookPointer hook);
35 42
36} // namespace Memory 43} // namespace Memory
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index c716a462b..4afd6c8a3 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -18,13 +18,13 @@ using std::chrono::microseconds;
18namespace Core { 18namespace Core {
19 19
20void PerfStats::BeginSystemFrame() { 20void PerfStats::BeginSystemFrame() {
21 std::lock_guard<std::mutex> lock(object_mutex); 21 std::lock_guard lock{object_mutex};
22 22
23 frame_begin = Clock::now(); 23 frame_begin = Clock::now();
24} 24}
25 25
26void PerfStats::EndSystemFrame() { 26void PerfStats::EndSystemFrame() {
27 std::lock_guard<std::mutex> lock(object_mutex); 27 std::lock_guard lock{object_mutex};
28 28
29 auto frame_end = Clock::now(); 29 auto frame_end = Clock::now();
30 accumulated_frametime += frame_end - frame_begin; 30 accumulated_frametime += frame_end - frame_begin;
@@ -35,13 +35,13 @@ void PerfStats::EndSystemFrame() {
35} 35}
36 36
37void PerfStats::EndGameFrame() { 37void PerfStats::EndGameFrame() {
38 std::lock_guard<std::mutex> lock(object_mutex); 38 std::lock_guard lock{object_mutex};
39 39
40 game_frames += 1; 40 game_frames += 1;
41} 41}
42 42
43PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { 43PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
44 std::lock_guard<std::mutex> lock(object_mutex); 44 std::lock_guard lock{object_mutex};
45 45
46 const auto now = Clock::now(); 46 const auto now = Clock::now();
47 // Walltime elapsed since stats were reset 47 // Walltime elapsed since stats were reset
@@ -67,7 +67,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
67} 67}
68 68
69double PerfStats::GetLastFrameTimeScale() { 69double PerfStats::GetLastFrameTimeScale() {
70 std::lock_guard<std::mutex> lock(object_mutex); 70 std::lock_guard lock{object_mutex};
71 71
72 constexpr double FRAME_LENGTH = 1.0 / 60; 72 constexpr double FRAME_LENGTH = 1.0 / 60;
73 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; 73 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 6dd3139cc..6d32ebea3 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -82,7 +82,6 @@ void LogSetting(const std::string& name, const T& value) {
82void LogSettings() { 82void LogSettings() {
83 LOG_INFO(Config, "yuzu Configuration:"); 83 LOG_INFO(Config, "yuzu Configuration:");
84 LogSetting("System_UseDockedMode", Settings::values.use_docked_mode); 84 LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
85 LogSetting("System_EnableNfc", Settings::values.enable_nfc);
86 LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0)); 85 LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
87 LogSetting("System_CurrentUser", Settings::values.current_user); 86 LogSetting("System_CurrentUser", Settings::values.current_user);
88 LogSetting("System_LanguageIndex", Settings::values.language_index); 87 LogSetting("System_LanguageIndex", Settings::values.language_index);
diff --git a/src/core/settings.h b/src/core/settings.h
index cdfb2f742..d543eb32f 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -349,7 +349,6 @@ struct TouchscreenInput {
349struct Values { 349struct Values {
350 // System 350 // System
351 bool use_docked_mode; 351 bool use_docked_mode;
352 bool enable_nfc;
353 std::optional<u32> rng_seed; 352 std::optional<u32> rng_seed;
354 // Measured in seconds since epoch 353 // Measured in seconds since epoch
355 std::optional<std::chrono::seconds> custom_rtc; 354 std::optional<std::chrono::seconds> custom_rtc;
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
index 525fe6abc..078374be5 100644
--- a/src/input_common/keyboard.cpp
+++ b/src/input_common/keyboard.cpp
@@ -36,18 +36,18 @@ struct KeyButtonPair {
36class KeyButtonList { 36class KeyButtonList {
37public: 37public:
38 void AddKeyButton(int key_code, KeyButton* key_button) { 38 void AddKeyButton(int key_code, KeyButton* key_button) {
39 std::lock_guard<std::mutex> guard(mutex); 39 std::lock_guard guard{mutex};
40 list.push_back(KeyButtonPair{key_code, key_button}); 40 list.push_back(KeyButtonPair{key_code, key_button});
41 } 41 }
42 42
43 void RemoveKeyButton(const KeyButton* key_button) { 43 void RemoveKeyButton(const KeyButton* key_button) {
44 std::lock_guard<std::mutex> guard(mutex); 44 std::lock_guard guard{mutex};
45 list.remove_if( 45 list.remove_if(
46 [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; }); 46 [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; });
47 } 47 }
48 48
49 void ChangeKeyStatus(int key_code, bool pressed) { 49 void ChangeKeyStatus(int key_code, bool pressed) {
50 std::lock_guard<std::mutex> guard(mutex); 50 std::lock_guard guard{mutex};
51 for (const KeyButtonPair& pair : list) { 51 for (const KeyButtonPair& pair : list) {
52 if (pair.key_code == key_code) 52 if (pair.key_code == key_code)
53 pair.key_button->status.store(pressed); 53 pair.key_button->status.store(pressed);
@@ -55,7 +55,7 @@ public:
55 } 55 }
56 56
57 void ChangeAllKeyStatus(bool pressed) { 57 void ChangeAllKeyStatus(bool pressed) {
58 std::lock_guard<std::mutex> guard(mutex); 58 std::lock_guard guard{mutex};
59 for (const KeyButtonPair& pair : list) { 59 for (const KeyButtonPair& pair : list) {
60 pair.key_button->status.store(pressed); 60 pair.key_button->status.store(pressed);
61 } 61 }
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
index 6d96d4019..868251628 100644
--- a/src/input_common/motion_emu.cpp
+++ b/src/input_common/motion_emu.cpp
@@ -39,7 +39,7 @@ public:
39 void Tilt(int x, int y) { 39 void Tilt(int x, int y) {
40 auto mouse_move = Common::MakeVec(x, y) - mouse_origin; 40 auto mouse_move = Common::MakeVec(x, y) - mouse_origin;
41 if (is_tilting) { 41 if (is_tilting) {
42 std::lock_guard<std::mutex> guard(tilt_mutex); 42 std::lock_guard guard{tilt_mutex};
43 if (mouse_move.x == 0 && mouse_move.y == 0) { 43 if (mouse_move.x == 0 && mouse_move.y == 0) {
44 tilt_angle = 0; 44 tilt_angle = 0;
45 } else { 45 } else {
@@ -51,13 +51,13 @@ public:
51 } 51 }
52 52
53 void EndTilt() { 53 void EndTilt() {
54 std::lock_guard<std::mutex> guard(tilt_mutex); 54 std::lock_guard guard{tilt_mutex};
55 tilt_angle = 0; 55 tilt_angle = 0;
56 is_tilting = false; 56 is_tilting = false;
57 } 57 }
58 58
59 std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() { 59 std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() {
60 std::lock_guard<std::mutex> guard(status_mutex); 60 std::lock_guard guard{status_mutex};
61 return status; 61 return status;
62 } 62 }
63 63
@@ -93,7 +93,7 @@ private:
93 old_q = q; 93 old_q = q;
94 94
95 { 95 {
96 std::lock_guard<std::mutex> guard(tilt_mutex); 96 std::lock_guard guard{tilt_mutex};
97 97
98 // Find the quaternion describing current 3DS tilting 98 // Find the quaternion describing current 3DS tilting
99 q = Common::MakeQuaternion( 99 q = Common::MakeQuaternion(
@@ -115,7 +115,7 @@ private:
115 115
116 // Update the sensor state 116 // Update the sensor state
117 { 117 {
118 std::lock_guard<std::mutex> guard(status_mutex); 118 std::lock_guard guard{status_mutex};
119 status = std::make_tuple(gravity, angular_rate); 119 status = std::make_tuple(gravity, angular_rate);
120 } 120 }
121 } 121 }
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 02a8d2e2c..d7f24c68a 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -24,17 +24,19 @@ namespace InputCommon::SDL {
24 24
25class State { 25class State {
26public: 26public:
27 /// Unresisters SDL device factories and shut them down. 27 using Pollers = std::vector<std::unique_ptr<Polling::DevicePoller>>;
28
29 /// Unregisters SDL device factories and shut them down.
28 virtual ~State() = default; 30 virtual ~State() = default;
29 31
30 virtual std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( 32 virtual Pollers GetPollers(Polling::DeviceType type) = 0;
31 InputCommon::Polling::DeviceType type) = 0;
32}; 33};
33 34
34class NullState : public State { 35class NullState : public State {
35public: 36public:
36 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( 37 Pollers GetPollers(Polling::DeviceType type) override {
37 InputCommon::Polling::DeviceType type) override {} 38 return {};
39 }
38}; 40};
39 41
40std::unique_ptr<State> Init(); 42std::unique_ptr<State> Init();
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 934339d3b..5949ecbae 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -55,22 +55,22 @@ public:
55 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {} 55 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
56 56
57 void SetButton(int button, bool value) { 57 void SetButton(int button, bool value) {
58 std::lock_guard<std::mutex> lock(mutex); 58 std::lock_guard lock{mutex};
59 state.buttons[button] = value; 59 state.buttons[button] = value;
60 } 60 }
61 61
62 bool GetButton(int button) const { 62 bool GetButton(int button) const {
63 std::lock_guard<std::mutex> lock(mutex); 63 std::lock_guard lock{mutex};
64 return state.buttons.at(button); 64 return state.buttons.at(button);
65 } 65 }
66 66
67 void SetAxis(int axis, Sint16 value) { 67 void SetAxis(int axis, Sint16 value) {
68 std::lock_guard<std::mutex> lock(mutex); 68 std::lock_guard lock{mutex};
69 state.axes[axis] = value; 69 state.axes[axis] = value;
70 } 70 }
71 71
72 float GetAxis(int axis) const { 72 float GetAxis(int axis) const {
73 std::lock_guard<std::mutex> lock(mutex); 73 std::lock_guard lock{mutex};
74 return state.axes.at(axis) / 32767.0f; 74 return state.axes.at(axis) / 32767.0f;
75 } 75 }
76 76
@@ -92,12 +92,12 @@ public:
92 } 92 }
93 93
94 void SetHat(int hat, Uint8 direction) { 94 void SetHat(int hat, Uint8 direction) {
95 std::lock_guard<std::mutex> lock(mutex); 95 std::lock_guard lock{mutex};
96 state.hats[hat] = direction; 96 state.hats[hat] = direction;
97 } 97 }
98 98
99 bool GetHatDirection(int hat, Uint8 direction) const { 99 bool GetHatDirection(int hat, Uint8 direction) const {
100 std::lock_guard<std::mutex> lock(mutex); 100 std::lock_guard lock{mutex};
101 return (state.hats.at(hat) & direction) != 0; 101 return (state.hats.at(hat) & direction) != 0;
102 } 102 }
103 /** 103 /**
@@ -140,7 +140,7 @@ private:
140 * Get the nth joystick with the corresponding GUID 140 * Get the nth joystick with the corresponding GUID
141 */ 141 */
142std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { 142std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
143 std::lock_guard<std::mutex> lock(joystick_map_mutex); 143 std::lock_guard lock{joystick_map_mutex};
144 const auto it = joystick_map.find(guid); 144 const auto it = joystick_map.find(guid);
145 if (it != joystick_map.end()) { 145 if (it != joystick_map.end()) {
146 while (it->second.size() <= port) { 146 while (it->second.size() <= port) {
@@ -161,7 +161,8 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
161std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 161std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
162 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 162 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
163 const std::string guid = GetGUID(sdl_joystick); 163 const std::string guid = GetGUID(sdl_joystick);
164 std::lock_guard<std::mutex> lock(joystick_map_mutex); 164
165 std::lock_guard lock{joystick_map_mutex};
165 auto map_it = joystick_map.find(guid); 166 auto map_it = joystick_map.find(guid);
166 if (map_it != joystick_map.end()) { 167 if (map_it != joystick_map.end()) {
167 auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), 168 auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
@@ -198,8 +199,9 @@ void SDLState::InitJoystick(int joystick_index) {
198 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 199 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
199 return; 200 return;
200 } 201 }
201 std::string guid = GetGUID(sdl_joystick); 202 const std::string guid = GetGUID(sdl_joystick);
202 std::lock_guard<std::mutex> lock(joystick_map_mutex); 203
204 std::lock_guard lock{joystick_map_mutex};
203 if (joystick_map.find(guid) == joystick_map.end()) { 205 if (joystick_map.find(guid) == joystick_map.end()) {
204 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 206 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
205 joystick_map[guid].emplace_back(std::move(joystick)); 207 joystick_map[guid].emplace_back(std::move(joystick));
@@ -221,7 +223,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
221 std::string guid = GetGUID(sdl_joystick); 223 std::string guid = GetGUID(sdl_joystick);
222 std::shared_ptr<SDLJoystick> joystick; 224 std::shared_ptr<SDLJoystick> joystick;
223 { 225 {
224 std::lock_guard<std::mutex> lock(joystick_map_mutex); 226 std::lock_guard lock{joystick_map_mutex};
225 // This call to guid is safe since the joystick is guaranteed to be in the map 227 // This call to guid is safe since the joystick is guaranteed to be in the map
226 auto& joystick_guid_list = joystick_map[guid]; 228 auto& joystick_guid_list = joystick_map[guid];
227 const auto joystick_it = 229 const auto joystick_it =
@@ -274,7 +276,7 @@ void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
274} 276}
275 277
276void SDLState::CloseJoysticks() { 278void SDLState::CloseJoysticks() {
277 std::lock_guard<std::mutex> lock(joystick_map_mutex); 279 std::lock_guard lock{joystick_map_mutex};
278 joystick_map.clear(); 280 joystick_map.clear();
279} 281}
280 282
@@ -475,12 +477,11 @@ SDLState::SDLState() {
475 477
476 initialized = true; 478 initialized = true;
477 if (start_thread) { 479 if (start_thread) {
478 poll_thread = std::thread([&] { 480 poll_thread = std::thread([this] {
479 using namespace std::chrono_literals; 481 using namespace std::chrono_literals;
480 SDL_Event event;
481 while (initialized) { 482 while (initialized) {
482 SDL_PumpEvents(); 483 SDL_PumpEvents();
483 std::this_thread::sleep_for(std::chrono::duration(10ms)); 484 std::this_thread::sleep_for(10ms);
484 } 485 }
485 }); 486 });
486 } 487 }
@@ -651,9 +652,9 @@ private:
651}; 652};
652} // namespace Polling 653} // namespace Polling
653 654
654std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPollers( 655SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
655 InputCommon::Polling::DeviceType type) { 656 Pollers pollers;
656 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers; 657
657 switch (type) { 658 switch (type) {
658 case InputCommon::Polling::DeviceType::Analog: 659 case InputCommon::Polling::DeviceType::Analog:
659 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); 660 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
@@ -661,8 +662,9 @@ std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPo
661 case InputCommon::Polling::DeviceType::Button: 662 case InputCommon::Polling::DeviceType::Button:
662 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 663 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
663 break; 664 break;
664 return pollers;
665 } 665 }
666
667 return pollers;
666} 668}
667 669
668} // namespace SDL 670} // namespace SDL
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index fec82fbe6..2579741d6 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -25,7 +25,7 @@ public:
25 /// Initializes and registers SDL device factories 25 /// Initializes and registers SDL device factories
26 SDLState(); 26 SDLState();
27 27
28 /// Unresisters SDL device factories and shut them down. 28 /// Unregisters SDL device factories and shut them down.
29 ~SDLState() override; 29 ~SDLState() override;
30 30
31 /// Handle SDL_Events for joysticks from SDL_PollEvent 31 /// Handle SDL_Events for joysticks from SDL_PollEvent
@@ -35,8 +35,7 @@ public:
35 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); 35 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
36 36
37 /// Get all DevicePoller that use the SDL backend for a specific device type 37 /// Get all DevicePoller that use the SDL backend for a specific device type
38 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( 38 Pollers GetPollers(Polling::DeviceType type) override;
39 InputCommon::Polling::DeviceType type) override;
40 39
41 /// Used by the Pollers during config 40 /// Used by the Pollers during config
42 std::atomic<bool> polling = false; 41 std::atomic<bool> polling = false;
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 37f09ce5f..c7038b217 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,4 +1,7 @@
1add_executable(tests 1add_executable(tests
2 common/bit_field.cpp
3 common/bit_utils.cpp
4 common/multi_level_queue.cpp
2 common/param_package.cpp 5 common/param_package.cpp
3 common/ring_buffer.cpp 6 common/ring_buffer.cpp
4 core/arm/arm_test_common.cpp 7 core/arm/arm_test_common.cpp
diff --git a/src/tests/common/bit_field.cpp b/src/tests/common/bit_field.cpp
new file mode 100644
index 000000000..8ca1889f9
--- /dev/null
+++ b/src/tests/common/bit_field.cpp
@@ -0,0 +1,90 @@
1// Copyright 2019 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cstring>
7#include <type_traits>
8#include <catch2/catch.hpp>
9#include "common/bit_field.h"
10
11TEST_CASE("BitField", "[common]") {
12 enum class TestEnum : u32 {
13 A = 0b10111101,
14 B = 0b10101110,
15 C = 0b00001111,
16 };
17
18 union LEBitField {
19 u32_le raw;
20 BitField<0, 6, u32> a;
21 BitField<6, 4, s32> b;
22 BitField<10, 8, TestEnum> c;
23 BitField<18, 14, u32> d;
24 } le_bitfield;
25
26 union BEBitField {
27 u32_be raw;
28 BitFieldBE<0, 6, u32> a;
29 BitFieldBE<6, 4, s32> b;
30 BitFieldBE<10, 8, TestEnum> c;
31 BitFieldBE<18, 14, u32> d;
32 } be_bitfield;
33
34 static_assert(sizeof(LEBitField) == sizeof(u32));
35 static_assert(sizeof(BEBitField) == sizeof(u32));
36 static_assert(std::is_trivially_copyable_v<LEBitField>);
37 static_assert(std::is_trivially_copyable_v<BEBitField>);
38
39 std::array<u8, 4> raw{{
40 0b01101100,
41 0b11110110,
42 0b10111010,
43 0b11101100,
44 }};
45
46 std::memcpy(&le_bitfield, &raw, sizeof(raw));
47 std::memcpy(&be_bitfield, &raw, sizeof(raw));
48
49 // bit fields: 11101100101110'10111101'1001'101100
50 REQUIRE(le_bitfield.raw == 0b11101100'10111010'11110110'01101100);
51 REQUIRE(le_bitfield.a == 0b101100);
52 REQUIRE(le_bitfield.b == -7); // 1001 as two's complement
53 REQUIRE(le_bitfield.c == TestEnum::A);
54 REQUIRE(le_bitfield.d == 0b11101100101110);
55
56 le_bitfield.a.Assign(0b000111);
57 le_bitfield.b.Assign(-1);
58 le_bitfield.c.Assign(TestEnum::C);
59 le_bitfield.d.Assign(0b01010101010101);
60 std::memcpy(&raw, &le_bitfield, sizeof(raw));
61 // bit fields: 01010101010101'00001111'1111'000111
62 REQUIRE(le_bitfield.raw == 0b01010101'01010100'00111111'11000111);
63 REQUIRE(raw == std::array<u8, 4>{{
64 0b11000111,
65 0b00111111,
66 0b01010100,
67 0b01010101,
68 }});
69
70 // bit fields: 01101100111101'10101110'1011'101100
71 REQUIRE(be_bitfield.raw == 0b01101100'11110110'10111010'11101100);
72 REQUIRE(be_bitfield.a == 0b101100);
73 REQUIRE(be_bitfield.b == -5); // 1011 as two's complement
74 REQUIRE(be_bitfield.c == TestEnum::B);
75 REQUIRE(be_bitfield.d == 0b01101100111101);
76
77 be_bitfield.a.Assign(0b000111);
78 be_bitfield.b.Assign(-1);
79 be_bitfield.c.Assign(TestEnum::C);
80 be_bitfield.d.Assign(0b01010101010101);
81 std::memcpy(&raw, &be_bitfield, sizeof(raw));
82 // bit fields: 01010101010101'00001111'1111'000111
83 REQUIRE(be_bitfield.raw == 0b01010101'01010100'00111111'11000111);
84 REQUIRE(raw == std::array<u8, 4>{{
85 0b01010101,
86 0b01010100,
87 0b00111111,
88 0b11000111,
89 }});
90}
diff --git a/src/tests/common/bit_utils.cpp b/src/tests/common/bit_utils.cpp
new file mode 100644
index 000000000..479b5995a
--- /dev/null
+++ b/src/tests/common/bit_utils.cpp
@@ -0,0 +1,23 @@
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 <catch2/catch.hpp>
6#include <math.h>
7#include "common/bit_util.h"
8
9namespace Common {
10
11TEST_CASE("BitUtils::CountTrailingZeroes", "[common]") {
12 REQUIRE(Common::CountTrailingZeroes32(0) == 32);
13 REQUIRE(Common::CountTrailingZeroes64(0) == 64);
14 REQUIRE(Common::CountTrailingZeroes32(9) == 0);
15 REQUIRE(Common::CountTrailingZeroes32(8) == 3);
16 REQUIRE(Common::CountTrailingZeroes32(0x801000) == 12);
17 REQUIRE(Common::CountTrailingZeroes64(9) == 0);
18 REQUIRE(Common::CountTrailingZeroes64(8) == 3);
19 REQUIRE(Common::CountTrailingZeroes64(0x801000) == 12);
20 REQUIRE(Common::CountTrailingZeroes64(0x801000000000UL) == 36);
21}
22
23} // namespace Common
diff --git a/src/tests/common/multi_level_queue.cpp b/src/tests/common/multi_level_queue.cpp
new file mode 100644
index 000000000..cca7ec7da
--- /dev/null
+++ b/src/tests/common/multi_level_queue.cpp
@@ -0,0 +1,55 @@
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 <catch2/catch.hpp>
6#include <math.h>
7#include "common/common_types.h"
8#include "common/multi_level_queue.h"
9
10namespace Common {
11
12TEST_CASE("MultiLevelQueue", "[common]") {
13 std::array<f32, 8> values = {0.0, 5.0, 1.0, 9.0, 8.0, 2.0, 6.0, 7.0};
14 Common::MultiLevelQueue<f32, 64> mlq;
15 REQUIRE(mlq.empty());
16 mlq.add(values[2], 2);
17 mlq.add(values[7], 7);
18 mlq.add(values[3], 3);
19 mlq.add(values[4], 4);
20 mlq.add(values[0], 0);
21 mlq.add(values[5], 5);
22 mlq.add(values[6], 6);
23 mlq.add(values[1], 1);
24 u32 index = 0;
25 bool all_set = true;
26 for (auto& f : mlq) {
27 all_set &= (f == values[index]);
28 index++;
29 }
30 REQUIRE(all_set);
31 REQUIRE(!mlq.empty());
32 f32 v = 8.0;
33 mlq.add(v, 2);
34 v = -7.0;
35 mlq.add(v, 2, false);
36 REQUIRE(mlq.front(2) == -7.0);
37 mlq.yield(2);
38 REQUIRE(mlq.front(2) == values[2]);
39 REQUIRE(mlq.back(2) == -7.0);
40 REQUIRE(mlq.empty(8));
41 v = 10.0;
42 mlq.add(v, 8);
43 mlq.adjust(v, 8, 9);
44 REQUIRE(mlq.front(9) == v);
45 REQUIRE(mlq.empty(8));
46 REQUIRE(!mlq.empty(9));
47 mlq.adjust(values[0], 0, 9);
48 REQUIRE(mlq.highest_priority_set() == 1);
49 REQUIRE(mlq.lowest_priority_set() == 9);
50 mlq.remove(values[1], 1);
51 REQUIRE(mlq.highest_priority_set() == 2);
52 REQUIRE(mlq.empty(1));
53}
54
55} // namespace Common
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 6fe56833d..3e1a735c3 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6 6
7#include "common/page_table.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/hle/kernel/process.h" 9#include "core/hle/kernel/process.h"
9#include "core/memory.h" 10#include "core/memory.h"
@@ -22,7 +23,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
22 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); 23 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
23 page_table->special_regions.clear(); 24 page_table->special_regions.clear();
24 std::fill(page_table->attributes.begin(), page_table->attributes.end(), 25 std::fill(page_table->attributes.begin(), page_table->attributes.end(),
25 Memory::PageType::Unmapped); 26 Common::PageType::Unmapped);
26 27
27 Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); 28 Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
28 Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); 29 Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h
index 0b7539601..d145dbfcc 100644
--- a/src/tests/core/arm/arm_test_common.h
+++ b/src/tests/core/arm/arm_test_common.h
@@ -9,10 +9,10 @@
9#include <vector> 9#include <vector>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/memory_hook.h"
12#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
13#include "core/memory_hook.h"
14 14
15namespace Memory { 15namespace Common {
16struct PageTable; 16struct PageTable;
17} 17}
18 18
@@ -58,7 +58,7 @@ public:
58 58
59private: 59private:
60 friend struct TestMemory; 60 friend struct TestMemory;
61 struct TestMemory final : Memory::MemoryHook { 61 struct TestMemory final : Common::MemoryHook {
62 explicit TestMemory(TestEnvironment* env_) : env(env_) {} 62 explicit TestMemory(TestEnvironment* env_) : env(env_) {}
63 TestEnvironment* env; 63 TestEnvironment* env;
64 64
@@ -86,7 +86,7 @@ private:
86 bool mutable_memory; 86 bool mutable_memory;
87 std::shared_ptr<TestMemory> test_memory; 87 std::shared_ptr<TestMemory> test_memory;
88 std::vector<WriteRecord> write_records; 88 std::vector<WriteRecord> write_records;
89 Memory::PageTable* page_table = nullptr; 89 Common::PageTable* page_table = nullptr;
90 Kernel::KernelCore kernel; 90 Kernel::KernelCore kernel;
91}; 91};
92 92
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 14b76680f..242a0d1cd 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -128,7 +128,9 @@ if (ENABLE_VULKAN)
128 renderer_vulkan/vk_scheduler.cpp 128 renderer_vulkan/vk_scheduler.cpp
129 renderer_vulkan/vk_scheduler.h 129 renderer_vulkan/vk_scheduler.h
130 renderer_vulkan/vk_stream_buffer.cpp 130 renderer_vulkan/vk_stream_buffer.cpp
131 renderer_vulkan/vk_stream_buffer.h) 131 renderer_vulkan/vk_stream_buffer.h
132 renderer_vulkan/vk_swapchain.cpp
133 renderer_vulkan/vk_swapchain.h)
132 134
133 target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) 135 target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include)
134 target_compile_definitions(video_core PRIVATE HAS_VULKAN) 136 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
@@ -137,4 +139,4 @@ endif()
137create_target_directory_groups(video_core) 139create_target_directory_groups(video_core)
138 140
139target_link_libraries(video_core PUBLIC common core) 141target_link_libraries(video_core PUBLIC common core)
140target_link_libraries(video_core PRIVATE glad lz4_static) 142target_link_libraries(video_core PRIVATE glad)
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 5ffb492ea..f0ef67535 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -10,7 +10,7 @@ namespace Tegra {
10 10
11void DebugContext::DoOnEvent(Event event, void* data) { 11void DebugContext::DoOnEvent(Event event, void* data) {
12 { 12 {
13 std::unique_lock<std::mutex> lock(breakpoint_mutex); 13 std::unique_lock lock{breakpoint_mutex};
14 14
15 // TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will 15 // TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will
16 // show on debug widgets 16 // show on debug widgets
@@ -32,7 +32,7 @@ void DebugContext::DoOnEvent(Event event, void* data) {
32 32
33void DebugContext::Resume() { 33void DebugContext::Resume() {
34 { 34 {
35 std::lock_guard<std::mutex> lock(breakpoint_mutex); 35 std::lock_guard lock{breakpoint_mutex};
36 36
37 // Tell all observers that we are about to resume 37 // Tell all observers that we are about to resume
38 for (auto& breakpoint_observer : breakpoint_observers) { 38 for (auto& breakpoint_observer : breakpoint_observers) {
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index c235faf46..ac3a2eb01 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -40,7 +40,7 @@ public:
40 /// Constructs the object such that it observes events of the given DebugContext. 40 /// Constructs the object such that it observes events of the given DebugContext.
41 explicit BreakPointObserver(std::shared_ptr<DebugContext> debug_context) 41 explicit BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
42 : context_weak(debug_context) { 42 : context_weak(debug_context) {
43 std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex); 43 std::unique_lock lock{debug_context->breakpoint_mutex};
44 debug_context->breakpoint_observers.push_back(this); 44 debug_context->breakpoint_observers.push_back(this);
45 } 45 }
46 46
@@ -48,7 +48,7 @@ public:
48 auto context = context_weak.lock(); 48 auto context = context_weak.lock();
49 if (context) { 49 if (context) {
50 { 50 {
51 std::unique_lock<std::mutex> lock(context->breakpoint_mutex); 51 std::unique_lock lock{context->breakpoint_mutex};
52 context->breakpoint_observers.remove(this); 52 context->breakpoint_observers.remove(this);
53 } 53 }
54 54
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index bff1a37ff..8b1bea1ae 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -55,12 +55,9 @@ bool DmaPusher::Step() {
55 } 55 }
56 56
57 // Push buffer non-empty, read a word 57 // Push buffer non-empty, read a word
58 const auto address = gpu.MemoryManager().GpuToCpuAddress(dma_get);
59 ASSERT_MSG(address, "Invalid GPU address");
60
61 command_headers.resize(command_list_header.size); 58 command_headers.resize(command_list_header.size);
62 59 gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(),
63 Memory::ReadBlock(*address, command_headers.data(), command_list_header.size * sizeof(u32)); 60 command_list_header.size * sizeof(u32));
64 61
65 for (const CommandHeader& command_header : command_headers) { 62 for (const CommandHeader& command_header : command_headers) {
66 63
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 27a36348c..6ab06518f 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -9,7 +9,6 @@
9 9
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/memory_manager.h"
13 12
14namespace Tegra { 13namespace Tegra {
15 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 aae2a4019..cd51a31d7 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -5,16 +5,17 @@
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 13
13namespace Tegra::Engines { 14namespace Tegra::Engines {
14 15
15KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 16KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
16 MemoryManager& memory_manager) 17 MemoryManager& memory_manager)
17 : system{system}, memory_manager(memory_manager), rasterizer{rasterizer} {} 18 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {}
18 19
19KeplerMemory::~KeplerMemory() = default; 20KeplerMemory::~KeplerMemory() = default;
20 21
@@ -40,17 +41,13 @@ void KeplerMemory::ProcessData(u32 data) {
40 ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported"); 41 ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported");
41 ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0); 42 ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0);
42 43
43 const GPUVAddr address = regs.dest.Address();
44 const auto dest_address =
45 memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
46 ASSERT_MSG(dest_address, "Invalid GPU address");
47
48 // We have to invalidate the destination region to evict any outdated surfaces from the cache. 44 // We have to invalidate the destination region to evict any outdated surfaces from the cache.
49 // We do this before actually writing the new data because the destination address might contain 45 // We do this before actually writing the new data because the destination address might
50 // a dirty surface that will have to be written back to memory. 46 // contain a dirty surface that will have to be written back to memory.
51 Core::System::GetInstance().GPU().InvalidateRegion(*dest_address, sizeof(u32)); 47 const GPUVAddr address{regs.dest.Address() + state.write_offset * sizeof(u32)};
48 rasterizer.InvalidateRegion(ToCacheAddr(memory_manager.GetPointer(address)), sizeof(u32));
49 memory_manager.Write<u32>(address, data);
52 50
53 Memory::Write32(*dest_address, data);
54 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); 51 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
55 52
56 state.write_offset++; 53 state.write_offset++;
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 144e7fa82..3c3ac8f81 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
@@ -270,11 +269,9 @@ void Maxwell3D::ProcessMacroBind(u32 data) {
270} 269}
271 270
272void Maxwell3D::ProcessQueryGet() { 271void Maxwell3D::ProcessQueryGet() {
273 GPUVAddr sequence_address = regs.query.QueryAddress(); 272 const GPUVAddr sequence_address{regs.query.QueryAddress()};
274 // Since the sequence address is given as a GPU VAddr, we have to convert it to an application 273 // Since the sequence address is given as a GPU VAddr, we have to convert it to an application
275 // VAddr before writing. 274 // VAddr before writing.
276 const auto address = memory_manager.GpuToCpuAddress(sequence_address);
277 ASSERT_MSG(address, "Invalid GPU address");
278 275
279 // TODO(Subv): Support the other query units. 276 // TODO(Subv): Support the other query units.
280 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, 277 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
@@ -309,7 +306,7 @@ void Maxwell3D::ProcessQueryGet() {
309 // Write the current query sequence to the sequence address. 306 // Write the current query sequence to the sequence address.
310 // TODO(Subv): Find out what happens if you use a long query type but mark it as a short 307 // TODO(Subv): Find out what happens if you use a long query type but mark it as a short
311 // query. 308 // query.
312 Memory::Write32(*address, sequence); 309 memory_manager.Write<u32>(sequence_address, sequence);
313 } else { 310 } else {
314 // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast 311 // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast
315 // GPU, this command may actually take a while to complete in real hardware due to GPU 312 // GPU, this command may actually take a while to complete in real hardware due to GPU
@@ -318,7 +315,7 @@ void Maxwell3D::ProcessQueryGet() {
318 query_result.value = result; 315 query_result.value = result;
319 // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming 316 // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
320 query_result.timestamp = system.CoreTiming().GetTicks(); 317 query_result.timestamp = system.CoreTiming().GetTicks();
321 Memory::WriteBlock(*address, &query_result, sizeof(query_result)); 318 memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result));
322 } 319 }
323 dirty_flags.OnMemoryWrite(); 320 dirty_flags.OnMemoryWrite();
324 break; 321 break;
@@ -393,10 +390,12 @@ void Maxwell3D::ProcessCBData(u32 value) {
393 // Don't allow writing past the end of the buffer. 390 // Don't allow writing past the end of the buffer.
394 ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size); 391 ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
395 392
396 const auto address = memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos); 393 const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos};
397 ASSERT_MSG(address, "Invalid GPU address"); 394
395 u8* ptr{memory_manager.GetPointer(address)};
396 rasterizer.InvalidateRegion(ToCacheAddr(ptr), sizeof(u32));
397 memory_manager.Write<u32>(address, value);
398 398
399 Memory::Write32(*address, value);
400 dirty_flags.OnMemoryWrite(); 399 dirty_flags.OnMemoryWrite();
401 400
402 // Increment the current buffer position. 401 // Increment the current buffer position.
@@ -404,14 +403,10 @@ void Maxwell3D::ProcessCBData(u32 value) {
404} 403}
405 404
406Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { 405Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
407 const GPUVAddr tic_base_address = regs.tic.TICAddress(); 406 const GPUVAddr tic_address_gpu{regs.tic.TICAddress() + tic_index * sizeof(Texture::TICEntry)};
408
409 const GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry);
410 const auto tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu);
411 ASSERT_MSG(tic_address_cpu, "Invalid GPU address");
412 407
413 Texture::TICEntry tic_entry; 408 Texture::TICEntry tic_entry;
414 Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry)); 409 memory_manager.ReadBlock(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
415 410
416 ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear || 411 ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear ||
417 tic_entry.header_version == Texture::TICHeaderVersion::Pitch, 412 tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
@@ -429,14 +424,10 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
429} 424}
430 425
431Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { 426Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
432 const GPUVAddr tsc_base_address = regs.tsc.TSCAddress(); 427 const GPUVAddr tsc_address_gpu{regs.tsc.TSCAddress() + tsc_index * sizeof(Texture::TSCEntry)};
433
434 const GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry);
435 const auto tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu);
436 ASSERT_MSG(tsc_address_cpu, "Invalid GPU address");
437 428
438 Texture::TSCEntry tsc_entry; 429 Texture::TSCEntry tsc_entry;
439 Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry)); 430 memory_manager.ReadBlock(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
440 return tsc_entry; 431 return tsc_entry;
441} 432}
442 433
@@ -455,10 +446,7 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
455 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; 446 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset;
456 current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { 447 current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) {
457 448
458 const auto address = memory_manager.GpuToCpuAddress(current_texture); 449 const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(current_texture)};
459 ASSERT_MSG(address, "Invalid GPU address");
460
461 const Texture::TextureHandle tex_handle{Memory::Read32(*address)};
462 450
463 Texture::FullTextureInfo tex_info{}; 451 Texture::FullTextureInfo tex_info{};
464 // TODO(Subv): Use the shader to determine which textures are actually accessed. 452 // TODO(Subv): Use the shader to determine which textures are actually accessed.
@@ -493,10 +481,7 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
493 481
494 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); 482 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
495 483
496 const auto tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address); 484 const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
497 ASSERT_MSG(tex_address_cpu, "Invalid GPU address");
498
499 const Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)};
500 485
501 Texture::FullTextureInfo tex_info{}; 486 Texture::FullTextureInfo tex_info{};
502 tex_info.index = static_cast<u32>(offset); 487 tex_info.index = static_cast<u32>(offset);
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 7fbf1026e..b352060a1 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}
@@ -1093,7 +1096,6 @@ public:
1093 }; 1096 };
1094 1097
1095 State state{}; 1098 State state{};
1096 MemoryManager& memory_manager;
1097 1099
1098 struct DirtyFlags { 1100 struct DirtyFlags {
1099 std::bitset<8> color_buffer{0xFF}; 1101 std::bitset<8> color_buffer{0xFF};
@@ -1141,6 +1143,8 @@ private:
1141 1143
1142 VideoCore::RasterizerInterface& rasterizer; 1144 VideoCore::RasterizerInterface& rasterizer;
1143 1145
1146 MemoryManager& memory_manager;
1147
1144 /// Start offsets of each macro in macro_memory 1148 /// Start offsets of each macro in macro_memory
1145 std::unordered_map<u32, u32> macro_offsets; 1149 std::unordered_map<u32, u32> macro_offsets;
1146 1150
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 9dfea5999..2426d0067 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -5,17 +5,18 @@
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/textures/decoders.h" 13#include "video_core/textures/decoders.h"
13 14
14namespace Tegra::Engines { 15namespace Tegra::Engines {
15 16
16MaxwellDMA::MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 17MaxwellDMA::MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
17 MemoryManager& memory_manager) 18 MemoryManager& memory_manager)
18 : memory_manager(memory_manager), system{system}, rasterizer{rasterizer} {} 19 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {}
19 20
20void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) { 21void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) {
21 ASSERT_MSG(method_call.method < Regs::NUM_REGS, 22 ASSERT_MSG(method_call.method < Regs::NUM_REGS,
@@ -42,11 +43,6 @@ void MaxwellDMA::HandleCopy() {
42 const GPUVAddr source = regs.src_address.Address(); 43 const GPUVAddr source = regs.src_address.Address();
43 const GPUVAddr dest = regs.dst_address.Address(); 44 const GPUVAddr dest = regs.dst_address.Address();
44 45
45 const auto source_cpu = memory_manager.GpuToCpuAddress(source);
46 const auto dest_cpu = memory_manager.GpuToCpuAddress(dest);
47 ASSERT_MSG(source_cpu, "Invalid source GPU address");
48 ASSERT_MSG(dest_cpu, "Invalid destination GPU address");
49
50 // TODO(Subv): Perform more research and implement all features of this engine. 46 // TODO(Subv): Perform more research and implement all features of this engine.
51 ASSERT(regs.exec.enable_swizzle == 0); 47 ASSERT(regs.exec.enable_swizzle == 0);
52 ASSERT(regs.exec.query_mode == Regs::QueryMode::None); 48 ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
@@ -69,7 +65,7 @@ void MaxwellDMA::HandleCopy() {
69 // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count, 65 // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
70 // y_count). 66 // y_count).
71 if (!regs.exec.enable_2d) { 67 if (!regs.exec.enable_2d) {
72 Memory::CopyBlock(*dest_cpu, *source_cpu, regs.x_count); 68 memory_manager.CopyBlock(dest, source, regs.x_count);
73 return; 69 return;
74 } 70 }
75 71
@@ -78,9 +74,9 @@ void MaxwellDMA::HandleCopy() {
78 // rectangle. There is no need to manually flush/invalidate the regions because 74 // rectangle. There is no need to manually flush/invalidate the regions because
79 // CopyBlock does that for us. 75 // CopyBlock does that for us.
80 for (u32 line = 0; line < regs.y_count; ++line) { 76 for (u32 line = 0; line < regs.y_count; ++line) {
81 const VAddr source_line = *source_cpu + line * regs.src_pitch; 77 const GPUVAddr source_line = source + line * regs.src_pitch;
82 const VAddr dest_line = *dest_cpu + line * regs.dst_pitch; 78 const GPUVAddr dest_line = dest + line * regs.dst_pitch;
83 Memory::CopyBlock(dest_line, source_line, regs.x_count); 79 memory_manager.CopyBlock(dest_line, source_line, regs.x_count);
84 } 80 }
85 return; 81 return;
86 } 82 }
@@ -89,15 +85,28 @@ void MaxwellDMA::HandleCopy() {
89 85
90 const std::size_t copy_size = regs.x_count * regs.y_count; 86 const std::size_t copy_size = regs.x_count * regs.y_count;
91 87
88 auto source_ptr{memory_manager.GetPointer(source)};
89 auto dst_ptr{memory_manager.GetPointer(dest)};
90
91 if (!source_ptr) {
92 LOG_ERROR(HW_GPU, "source_ptr is invalid");
93 return;
94 }
95
96 if (!dst_ptr) {
97 LOG_ERROR(HW_GPU, "dst_ptr is invalid");
98 return;
99 }
100
92 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { 101 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
93 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated 102 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
94 // copying. 103 // copying.
95 Core::System::GetInstance().GPU().FlushRegion(*source_cpu, src_size); 104 rasterizer.FlushRegion(ToCacheAddr(source_ptr), src_size);
96 105
97 // We have to invalidate the destination region to evict any outdated surfaces from the 106 // We have to invalidate the destination region to evict any outdated surfaces from the
98 // cache. We do this before actually writing the new data because the destination address 107 // cache. We do this before actually writing the new data because the destination address
99 // might contain a dirty surface that will have to be written back to memory. 108 // might contain a dirty surface that will have to be written back to memory.
100 Core::System::GetInstance().GPU().InvalidateRegion(*dest_cpu, dst_size); 109 rasterizer.InvalidateRegion(ToCacheAddr(dst_ptr), dst_size);
101 }; 110 };
102 111
103 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 112 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
@@ -110,8 +119,8 @@ void MaxwellDMA::HandleCopy() {
110 copy_size * src_bytes_per_pixel); 119 copy_size * src_bytes_per_pixel);
111 120
112 Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch, 121 Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
113 regs.src_params.size_x, src_bytes_per_pixel, *source_cpu, 122 regs.src_params.size_x, src_bytes_per_pixel, source_ptr, dst_ptr,
114 *dest_cpu, regs.src_params.BlockHeight(), regs.src_params.pos_x, 123 regs.src_params.BlockHeight(), regs.src_params.pos_x,
115 regs.src_params.pos_y); 124 regs.src_params.pos_y);
116 } else { 125 } else {
117 ASSERT(regs.dst_params.size_z == 1); 126 ASSERT(regs.dst_params.size_z == 1);
@@ -124,7 +133,7 @@ void MaxwellDMA::HandleCopy() {
124 133
125 // If the input is linear and the output is tiled, swizzle the input and copy it over. 134 // If the input is linear and the output is tiled, swizzle the input and copy it over.
126 Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, 135 Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
127 src_bpp, *dest_cpu, *source_cpu, regs.dst_params.BlockHeight()); 136 src_bpp, dst_ptr, source_ptr, regs.dst_params.BlockHeight());
128 } 137 }
129} 138}
130 139
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/gpu.cpp b/src/video_core/gpu.cpp
index 08abf8ac9..30b29e14d 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -12,6 +12,7 @@
12#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/engines/maxwell_dma.h" 13#include "video_core/engines/maxwell_dma.h"
14#include "video_core/gpu.h" 14#include "video_core/gpu.h"
15#include "video_core/memory_manager.h"
15#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
16 17
17namespace Tegra { 18namespace Tegra {
@@ -274,7 +275,6 @@ void GPU::ProcessSemaphoreTriggerMethod() {
274 const auto op = 275 const auto op =
275 static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask); 276 static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
276 if (op == GpuSemaphoreOperation::WriteLong) { 277 if (op == GpuSemaphoreOperation::WriteLong) {
277 auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
278 struct Block { 278 struct Block {
279 u32 sequence; 279 u32 sequence;
280 u32 zeros = 0; 280 u32 zeros = 0;
@@ -286,11 +286,10 @@ void GPU::ProcessSemaphoreTriggerMethod() {
286 // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of 286 // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
287 // CoreTiming 287 // CoreTiming
288 block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks(); 288 block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
289 Memory::WriteBlock(*address, &block, sizeof(block)); 289 memory_manager->WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block,
290 sizeof(block));
290 } else { 291 } else {
291 const auto address = 292 const u32 word{memory_manager->Read<u32>(regs.semaphore_address.SemaphoreAddress())};
292 memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
293 const u32 word = Memory::Read32(*address);
294 if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || 293 if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) ||
295 (op == GpuSemaphoreOperation::AcquireGequal && 294 (op == GpuSemaphoreOperation::AcquireGequal &&
296 static_cast<s32>(word - regs.semaphore_sequence) > 0) || 295 static_cast<s32>(word - regs.semaphore_sequence) > 0) ||
@@ -317,13 +316,11 @@ void GPU::ProcessSemaphoreTriggerMethod() {
317} 316}
318 317
319void GPU::ProcessSemaphoreRelease() { 318void GPU::ProcessSemaphoreRelease() {
320 const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress()); 319 memory_manager->Write<u32>(regs.semaphore_address.SemaphoreAddress(), regs.semaphore_release);
321 Memory::Write32(*address, regs.semaphore_release);
322} 320}
323 321
324void GPU::ProcessSemaphoreAcquire() { 322void GPU::ProcessSemaphoreAcquire() {
325 const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress()); 323 const u32 word = memory_manager->Read<u32>(regs.semaphore_address.SemaphoreAddress());
326 const u32 word = Memory::Read32(*address);
327 const auto value = regs.semaphore_acquire; 324 const auto value = regs.semaphore_acquire;
328 if (word != value) { 325 if (word != value) {
329 regs.acquire_active = true; 326 regs.acquire_active = true;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 56a203275..de30ea354 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -9,7 +9,11 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/service/nvflinger/buffer_queue.h" 10#include "core/hle/service/nvflinger/buffer_queue.h"
11#include "video_core/dma_pusher.h" 11#include "video_core/dma_pusher.h"
12#include "video_core/memory_manager.h" 12
13using CacheAddr = std::uintptr_t;
14inline CacheAddr ToCacheAddr(const void* host_ptr) {
15 return reinterpret_cast<CacheAddr>(host_ptr);
16}
13 17
14namespace Core { 18namespace Core {
15class System; 19class System;
@@ -119,6 +123,8 @@ enum class EngineID {
119 MAXWELL_DMA_COPY_A = 0xB0B5, 123 MAXWELL_DMA_COPY_A = 0xB0B5,
120}; 124};
121 125
126class MemoryManager;
127
122class GPU { 128class GPU {
123public: 129public:
124 explicit GPU(Core::System& system, VideoCore::RendererBase& renderer); 130 explicit GPU(Core::System& system, VideoCore::RendererBase& renderer);
@@ -171,11 +177,11 @@ public:
171 u32 address_high; 177 u32 address_high;
172 u32 address_low; 178 u32 address_low;
173 179
174 GPUVAddr SmaphoreAddress() const { 180 GPUVAddr SemaphoreAddress() const {
175 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 181 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
176 address_low); 182 address_low);
177 } 183 }
178 } smaphore_address; 184 } semaphore_address;
179 185
180 u32 semaphore_sequence; 186 u32 semaphore_sequence;
181 u32 semaphore_trigger; 187 u32 semaphore_trigger;
@@ -209,13 +215,13 @@ public:
209 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0; 215 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0;
210 216
211 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 217 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
212 virtual void FlushRegion(VAddr addr, u64 size) = 0; 218 virtual void FlushRegion(CacheAddr addr, u64 size) = 0;
213 219
214 /// Notify rasterizer that any caches of the specified region should be invalidated 220 /// Notify rasterizer that any caches of the specified region should be invalidated
215 virtual void InvalidateRegion(VAddr addr, u64 size) = 0; 221 virtual void InvalidateRegion(CacheAddr addr, u64 size) = 0;
216 222
217 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated 223 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
218 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 224 virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0;
219 225
220private: 226private:
221 void ProcessBindMethod(const MethodCall& method_call); 227 void ProcessBindMethod(const MethodCall& method_call);
@@ -239,9 +245,8 @@ protected:
239private: 245private:
240 std::unique_ptr<Tegra::MemoryManager> memory_manager; 246 std::unique_ptr<Tegra::MemoryManager> memory_manager;
241 247
242 /// Mapping of command subchannels to their bound engine ids. 248 /// Mapping of command subchannels to their bound engine ids
243 std::array<EngineID, 8> bound_engines = {}; 249 std::array<EngineID, 8> bound_engines = {};
244
245 /// 3D engine 250 /// 3D engine
246 std::unique_ptr<Engines::Maxwell3D> maxwell_3d; 251 std::unique_ptr<Engines::Maxwell3D> maxwell_3d;
247 /// 2D engine 252 /// 2D engine
@@ -258,7 +263,7 @@ private:
258 static_assert(offsetof(GPU::Regs, field_name) == position * 4, \ 263 static_assert(offsetof(GPU::Regs, field_name) == position * 4, \
259 "Field " #field_name " has invalid position") 264 "Field " #field_name " has invalid position")
260 265
261ASSERT_REG_POSITION(smaphore_address, 0x4); 266ASSERT_REG_POSITION(semaphore_address, 0x4);
262ASSERT_REG_POSITION(semaphore_sequence, 0x6); 267ASSERT_REG_POSITION(semaphore_sequence, 0x6);
263ASSERT_REG_POSITION(semaphore_trigger, 0x7); 268ASSERT_REG_POSITION(semaphore_trigger, 0x7);
264ASSERT_REG_POSITION(reference_count, 0x14); 269ASSERT_REG_POSITION(reference_count, 0x14);
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index ad0a747e3..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
@@ -22,15 +22,15 @@ void GPUAsynch::SwapBuffers(
22 gpu_thread.SwapBuffers(std::move(framebuffer)); 22 gpu_thread.SwapBuffers(std::move(framebuffer));
23} 23}
24 24
25void GPUAsynch::FlushRegion(VAddr addr, u64 size) { 25void GPUAsynch::FlushRegion(CacheAddr addr, u64 size) {
26 gpu_thread.FlushRegion(addr, size); 26 gpu_thread.FlushRegion(addr, size);
27} 27}
28 28
29void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) { 29void GPUAsynch::InvalidateRegion(CacheAddr addr, u64 size) {
30 gpu_thread.InvalidateRegion(addr, size); 30 gpu_thread.InvalidateRegion(addr, size);
31} 31}
32 32
33void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { 33void GPUAsynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
34 gpu_thread.FlushAndInvalidateRegion(addr, size); 34 gpu_thread.FlushAndInvalidateRegion(addr, size);
35} 35}
36 36
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index e6a807aba..1dcc61a6c 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -26,9 +26,9 @@ public:
26 void PushGPUEntries(Tegra::CommandList&& entries) override; 26 void PushGPUEntries(Tegra::CommandList&& entries) override;
27 void SwapBuffers( 27 void SwapBuffers(
28 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; 28 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
29 void FlushRegion(VAddr addr, u64 size) override; 29 void FlushRegion(CacheAddr addr, u64 size) override;
30 void InvalidateRegion(VAddr addr, u64 size) override; 30 void InvalidateRegion(CacheAddr addr, u64 size) override;
31 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 31 void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
32 32
33private: 33private:
34 GPUThread::ThreadManager gpu_thread; 34 GPUThread::ThreadManager gpu_thread;
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index 4c00b96c7..2cfc900ed 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -22,15 +22,15 @@ void GPUSynch::SwapBuffers(
22 renderer.SwapBuffers(std::move(framebuffer)); 22 renderer.SwapBuffers(std::move(framebuffer));
23} 23}
24 24
25void GPUSynch::FlushRegion(VAddr addr, u64 size) { 25void GPUSynch::FlushRegion(CacheAddr addr, u64 size) {
26 renderer.Rasterizer().FlushRegion(addr, size); 26 renderer.Rasterizer().FlushRegion(addr, size);
27} 27}
28 28
29void GPUSynch::InvalidateRegion(VAddr addr, u64 size) { 29void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) {
30 renderer.Rasterizer().InvalidateRegion(addr, size); 30 renderer.Rasterizer().InvalidateRegion(addr, size);
31} 31}
32 32
33void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { 33void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
34 renderer.Rasterizer().FlushAndInvalidateRegion(addr, size); 34 renderer.Rasterizer().FlushAndInvalidateRegion(addr, size);
35} 35}
36 36
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 7d5a241ff..766b5631c 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -21,9 +21,9 @@ public:
21 void PushGPUEntries(Tegra::CommandList&& entries) override; 21 void PushGPUEntries(Tegra::CommandList&& entries) override;
22 void SwapBuffers( 22 void SwapBuffers(
23 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; 23 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
24 void FlushRegion(VAddr addr, u64 size) override; 24 void FlushRegion(CacheAddr addr, u64 size) override;
25 void InvalidateRegion(VAddr addr, u64 size) override; 25 void InvalidateRegion(CacheAddr addr, u64 size) override;
26 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 26 void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
27}; 27};
28 28
29} // namespace VideoCommon 29} // namespace VideoCommon
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index c5bdd2a17..cc56cf467 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -4,8 +4,10 @@
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 "core/settings.h"
9#include "video_core/dma_pusher.h" 11#include "video_core/dma_pusher.h"
10#include "video_core/gpu.h" 12#include "video_core/gpu.h"
11#include "video_core/gpu_thread.h" 13#include "video_core/gpu_thread.h"
@@ -13,38 +15,13 @@
13 15
14namespace VideoCommon::GPUThread { 16namespace VideoCommon::GPUThread {
15 17
16/// Executes a single GPU thread command
17static void ExecuteCommand(CommandData* command, VideoCore::RendererBase& renderer,
18 Tegra::DmaPusher& dma_pusher) {
19 if (const auto submit_list = std::get_if<SubmitListCommand>(command)) {
20 dma_pusher.Push(std::move(submit_list->entries));
21 dma_pusher.DispatchCalls();
22 } else if (const auto data = std::get_if<SwapBuffersCommand>(command)) {
23 renderer.SwapBuffers(data->framebuffer);
24 } else if (const auto data = std::get_if<FlushRegionCommand>(command)) {
25 renderer.Rasterizer().FlushRegion(data->addr, data->size);
26 } else if (const auto data = std::get_if<InvalidateRegionCommand>(command)) {
27 renderer.Rasterizer().InvalidateRegion(data->addr, data->size);
28 } else if (const auto data = std::get_if<FlushAndInvalidateRegionCommand>(command)) {
29 renderer.Rasterizer().FlushAndInvalidateRegion(data->addr, data->size);
30 } else {
31 UNREACHABLE();
32 }
33}
34
35/// Runs the GPU thread 18/// Runs the GPU thread
36static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher, 19static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher,
37 SynchState& state) { 20 SynchState& state) {
38
39 MicroProfileOnThreadCreate("GpuThread"); 21 MicroProfileOnThreadCreate("GpuThread");
40 22
41 auto WaitForWakeup = [&]() {
42 std::unique_lock<std::mutex> lock{state.signal_mutex};
43 state.signal_condition.wait(lock, [&] { return !state.is_idle || !state.is_running; });
44 };
45
46 // Wait for first GPU command before acquiring the window context 23 // Wait for first GPU command before acquiring the window context
47 WaitForWakeup(); 24 state.WaitForCommands();
48 25
49 // If emulation was stopped during disk shader loading, abort before trying to acquire context 26 // If emulation was stopped during disk shader loading, abort before trying to acquire context
50 if (!state.is_running) { 27 if (!state.is_running) {
@@ -53,99 +30,91 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
53 30
54 Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()}; 31 Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()};
55 32
33 CommandDataContainer next;
56 while (state.is_running) { 34 while (state.is_running) {
57 if (!state.is_running) { 35 state.WaitForCommands();
58 return; 36 while (!state.queue.Empty()) {
59 } 37 state.queue.Pop(next);
60 38 if (const auto submit_list = std::get_if<SubmitListCommand>(&next.data)) {
61 { 39 dma_pusher.Push(std::move(submit_list->entries));
62 // Thread has been woken up, so make the previous write queue the next read queue 40 dma_pusher.DispatchCalls();
63 std::lock_guard<std::mutex> lock{state.signal_mutex}; 41 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
64 std::swap(state.push_queue, state.pop_queue); 42 renderer.SwapBuffers(std::move(data->framebuffer));
43 } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) {
44 renderer.Rasterizer().FlushRegion(data->addr, data->size);
45 } else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) {
46 renderer.Rasterizer().InvalidateRegion(data->addr, data->size);
47 } else if (const auto data = std::get_if<EndProcessingCommand>(&next.data)) {
48 return;
49 } else {
50 UNREACHABLE();
51 }
52 state.signaled_fence = next.fence;
53 state.TrySynchronize();
65 } 54 }
66
67 // Execute all of the GPU commands
68 while (!state.pop_queue->empty()) {
69 ExecuteCommand(&state.pop_queue->front(), renderer, dma_pusher);
70 state.pop_queue->pop();
71 }
72
73 state.UpdateIdleState();
74
75 // Signal that the GPU thread has finished processing commands
76 if (state.is_idle) {
77 state.idle_condition.notify_one();
78 }
79
80 // Wait for CPU thread to send more GPU commands
81 WaitForWakeup();
82 } 55 }
83} 56}
84 57
85ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) 58ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
86 : renderer{renderer}, dma_pusher{dma_pusher}, thread{RunThread, std::ref(renderer), 59 Tegra::DmaPusher& dma_pusher)
87 std::ref(dma_pusher), std::ref(state)}, 60 : system{system}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)} {
88 thread_id{thread.get_id()} {} 61 synchronization_event = system.CoreTiming().RegisterEvent(
62 "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
63}
89 64
90ThreadManager::~ThreadManager() { 65ThreadManager::~ThreadManager() {
91 { 66 // Notify GPU thread that a shutdown is pending
92 // Notify GPU thread that a shutdown is pending 67 PushCommand(EndProcessingCommand());
93 std::lock_guard<std::mutex> lock{state.signal_mutex};
94 state.is_running = false;
95 }
96
97 state.signal_condition.notify_one();
98 thread.join(); 68 thread.join();
99} 69}
100 70
101void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 71void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
102 if (entries.empty()) { 72 const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))};
103 return; 73 const s64 synchronization_ticks{Core::Timing::usToCycles(9000)};
104 } 74 system.CoreTiming().ScheduleEvent(synchronization_ticks, synchronization_event, fence);
105
106 PushCommand(SubmitListCommand(std::move(entries)), false, false);
107} 75}
108 76
109void ThreadManager::SwapBuffers( 77void ThreadManager::SwapBuffers(
110 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { 78 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
111 PushCommand(SwapBuffersCommand(std::move(framebuffer)), true, false); 79 PushCommand(SwapBuffersCommand(std::move(framebuffer)));
112} 80}
113 81
114void ThreadManager::FlushRegion(VAddr addr, u64 size) { 82void ThreadManager::FlushRegion(CacheAddr addr, u64 size) {
115 // Block the CPU when using accurate emulation 83 PushCommand(FlushRegionCommand(addr, size));
116 PushCommand(FlushRegionCommand(addr, size), Settings::values.use_accurate_gpu_emulation, false);
117} 84}
118 85
119void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { 86void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) {
120 PushCommand(InvalidateRegionCommand(addr, size), true, true); 87 if (state.queue.Empty()) {
88 // It's quicker to invalidate a single region on the CPU if the queue is already empty
89 system.Renderer().Rasterizer().InvalidateRegion(addr, size);
90 } else {
91 PushCommand(InvalidateRegionCommand(addr, size));
92 }
121} 93}
122 94
123void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { 95void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
96 // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important
124 InvalidateRegion(addr, size); 97 InvalidateRegion(addr, size);
125} 98}
126 99
127void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu) { 100u64 ThreadManager::PushCommand(CommandData&& command_data) {
128 { 101 const u64 fence{++state.last_fence};
129 std::lock_guard<std::mutex> lock{state.signal_mutex}; 102 state.queue.Push(CommandDataContainer(std::move(command_data), fence));
130 103 state.SignalCommands();
131 if ((allow_on_cpu && state.is_idle) || IsGpuThread()) { 104 return fence;
132 // Execute the command synchronously on the current thread 105}
133 ExecuteCommand(&command_data, renderer, dma_pusher);
134 return;
135 }
136 106
137 // Push the command to the GPU thread 107MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
138 state.UpdateIdleState(); 108void SynchState::WaitForSynchronization(u64 fence) {
139 state.push_queue->emplace(command_data); 109 if (signaled_fence >= fence) {
110 return;
140 } 111 }
141 112
142 // Signal the GPU thread that commands are pending 113 // Wait for the GPU to be idle (all commands to be executed)
143 state.signal_condition.notify_one(); 114 {
144 115 MICROPROFILE_SCOPE(GPU_wait);
145 if (wait_for_idle) { 116 std::unique_lock<std::mutex> lock{synchronization_mutex};
146 // Wait for the GPU to be idle (all commands to be executed) 117 synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; });
147 std::unique_lock<std::mutex> lock{state.idle_mutex};
148 state.idle_condition.wait(lock, [this] { return static_cast<bool>(state.is_idle); });
149 } 118 }
150} 119}
151 120
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index edb148b14..62bcea5bb 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -4,26 +4,33 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <atomic> 7#include <atomic>
9#include <condition_variable> 8#include <condition_variable>
10#include <memory>
11#include <mutex> 9#include <mutex>
12#include <optional> 10#include <optional>
13#include <thread> 11#include <thread>
14#include <variant> 12#include <variant>
15 13
14#include "common/threadsafe_queue.h"
15#include "video_core/gpu.h"
16
16namespace Tegra { 17namespace Tegra {
17struct FramebufferConfig; 18struct FramebufferConfig;
18class DmaPusher; 19class DmaPusher;
19} // namespace Tegra 20} // namespace Tegra
20 21
21namespace VideoCore { 22namespace Core {
22class RendererBase; 23class System;
23} // namespace VideoCore 24namespace Timing {
25struct EventType;
26} // namespace Timing
27} // namespace Core
24 28
25namespace VideoCommon::GPUThread { 29namespace VideoCommon::GPUThread {
26 30
31/// Command to signal to the GPU thread that processing has ended
32struct EndProcessingCommand final {};
33
27/// Command to signal to the GPU thread that a command list is ready for processing 34/// Command to signal to the GPU thread that a command list is ready for processing
28struct SubmitListCommand final { 35struct SubmitListCommand final {
29 explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {} 36 explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {}
@@ -36,65 +43,103 @@ struct SwapBuffersCommand final {
36 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer) 43 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer)
37 : framebuffer{std::move(framebuffer)} {} 44 : framebuffer{std::move(framebuffer)} {}
38 45
39 std::optional<const Tegra::FramebufferConfig> framebuffer; 46 std::optional<Tegra::FramebufferConfig> framebuffer;
40}; 47};
41 48
42/// Command to signal to the GPU thread to flush a region 49/// Command to signal to the GPU thread to flush a region
43struct FlushRegionCommand final { 50struct FlushRegionCommand final {
44 explicit constexpr FlushRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {} 51 explicit constexpr FlushRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {}
45 52
46 const VAddr addr; 53 CacheAddr addr;
47 const u64 size; 54 u64 size;
48}; 55};
49 56
50/// Command to signal to the GPU thread to invalidate a region 57/// Command to signal to the GPU thread to invalidate a region
51struct InvalidateRegionCommand final { 58struct InvalidateRegionCommand final {
52 explicit constexpr InvalidateRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {} 59 explicit constexpr InvalidateRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {}
53 60
54 const VAddr addr; 61 CacheAddr addr;
55 const u64 size; 62 u64 size;
56}; 63};
57 64
58/// Command to signal to the GPU thread to flush and invalidate a region 65/// Command to signal to the GPU thread to flush and invalidate a region
59struct FlushAndInvalidateRegionCommand final { 66struct FlushAndInvalidateRegionCommand final {
60 explicit constexpr FlushAndInvalidateRegionCommand(VAddr addr, u64 size) 67 explicit constexpr FlushAndInvalidateRegionCommand(CacheAddr addr, u64 size)
61 : addr{addr}, size{size} {} 68 : addr{addr}, size{size} {}
62 69
63 const VAddr addr; 70 CacheAddr addr;
64 const u64 size; 71 u64 size;
65}; 72};
66 73
67using CommandData = std::variant<SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, 74using CommandData =
68 InvalidateRegionCommand, FlushAndInvalidateRegionCommand>; 75 std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
76 InvalidateRegionCommand, FlushAndInvalidateRegionCommand>;
77
78struct CommandDataContainer {
79 CommandDataContainer() = default;
80
81 CommandDataContainer(CommandData&& data, u64 next_fence)
82 : data{std::move(data)}, fence{next_fence} {}
83
84 CommandDataContainer& operator=(const CommandDataContainer& t) {
85 data = std::move(t.data);
86 fence = t.fence;
87 return *this;
88 }
89
90 CommandData data;
91 u64 fence{};
92};
69 93
70/// Struct used to synchronize the GPU thread 94/// Struct used to synchronize the GPU thread
71struct SynchState final { 95struct SynchState final {
72 std::atomic<bool> is_running{true}; 96 std::atomic_bool is_running{true};
73 std::atomic<bool> is_idle{true}; 97 std::atomic_int queued_frame_count{};
74 std::condition_variable signal_condition; 98 std::mutex synchronization_mutex;
75 std::mutex signal_mutex; 99 std::mutex commands_mutex;
76 std::condition_variable idle_condition; 100 std::condition_variable commands_condition;
77 std::mutex idle_mutex; 101 std::condition_variable synchronization_condition;
78 102
79 // We use two queues for sending commands to the GPU thread, one for writing (push_queue) to and 103 /// Returns true if the gap in GPU commands is small enough that we can consider the CPU and GPU
80 // one for reading from (pop_queue). These are swapped whenever the current pop_queue becomes 104 /// synchronized. This is entirely empirical.
81 // empty. This allows for efficient thread-safe access, as it does not require any copies. 105 bool IsSynchronized() const {
82 106 constexpr std::size_t max_queue_gap{5};
83 using CommandQueue = std::queue<CommandData>; 107 return queue.Size() <= max_queue_gap;
84 std::array<CommandQueue, 2> command_queues; 108 }
85 CommandQueue* push_queue{&command_queues[0]}; 109
86 CommandQueue* pop_queue{&command_queues[1]}; 110 void TrySynchronize() {
87 111 if (IsSynchronized()) {
88 void UpdateIdleState() { 112 std::lock_guard<std::mutex> lock{synchronization_mutex};
89 std::lock_guard<std::mutex> lock{idle_mutex}; 113 synchronization_condition.notify_one();
90 is_idle = command_queues[0].empty() && command_queues[1].empty(); 114 }
91 } 115 }
116
117 void WaitForSynchronization(u64 fence);
118
119 void SignalCommands() {
120 if (queue.Empty()) {
121 return;
122 }
123
124 commands_condition.notify_one();
125 }
126
127 void WaitForCommands() {
128 std::unique_lock lock{commands_mutex};
129 commands_condition.wait(lock, [this] { return !queue.Empty(); });
130 }
131
132 using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
133 CommandQueue queue;
134 u64 last_fence{};
135 std::atomic<u64> signaled_fence{};
92}; 136};
93 137
94/// Class used to manage the GPU thread 138/// Class used to manage the GPU thread
95class ThreadManager final { 139class ThreadManager final {
96public: 140public:
97 explicit ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher); 141 explicit ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
142 Tegra::DmaPusher& dma_pusher);
98 ~ThreadManager(); 143 ~ThreadManager();
99 144
100 /// Push GPU command entries to be processed 145 /// Push GPU command entries to be processed
@@ -105,27 +150,22 @@ public:
105 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer); 150 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer);
106 151
107 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 152 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
108 void FlushRegion(VAddr addr, u64 size); 153 void FlushRegion(CacheAddr addr, u64 size);
109 154
110 /// Notify rasterizer that any caches of the specified region should be invalidated 155 /// Notify rasterizer that any caches of the specified region should be invalidated
111 void InvalidateRegion(VAddr addr, u64 size); 156 void InvalidateRegion(CacheAddr addr, u64 size);
112 157
113 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated 158 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
114 void FlushAndInvalidateRegion(VAddr addr, u64 size); 159 void FlushAndInvalidateRegion(CacheAddr addr, u64 size);
115 160
116private: 161private:
117 /// Pushes a command to be executed by the GPU thread 162 /// Pushes a command to be executed by the GPU thread
118 void PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu); 163 u64 PushCommand(CommandData&& command_data);
119
120 /// Returns true if this is called by the GPU thread
121 bool IsGpuThread() const {
122 return std::this_thread::get_id() == thread_id;
123 }
124 164
125private: 165private:
126 SynchState state; 166 SynchState state;
127 VideoCore::RendererBase& renderer; 167 Core::System& system;
128 Tegra::DmaPusher& dma_pusher; 168 Core::Timing::EventType* synchronization_event{};
129 std::thread thread; 169 std::thread thread;
130 std::thread::id thread_id; 170 std::thread::id thread_id;
131}; 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 54abe5298..8417324ff 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -5,181 +5,461 @@
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"
10#include "video_core/gpu.h"
8#include "video_core/memory_manager.h" 11#include "video_core/memory_manager.h"
12#include "video_core/rasterizer_interface.h"
13#include "video_core/renderer_base.h"
9 14
10namespace Tegra { 15namespace Tegra {
11 16
12MemoryManager::MemoryManager() { 17MemoryManager::MemoryManager() {
13 // Mark the first page as reserved, so that 0 is not a valid GPUVAddr. Otherwise, games might 18 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
14 // try to use 0 as a valid address, which is also used to mean nullptr. This fixes a bug with 19 std::fill(page_table.attributes.begin(), page_table.attributes.end(),
15 // Undertale using 0 for a render target. 20 Common::PageType::Unmapped);
16 PageSlot(0) = static_cast<u64>(PageStatus::Reserved); 21 page_table.Resize(address_space_width);
22
23 // Initialize the map with a single free region covering the entire managed space.
24 VirtualMemoryArea initial_vma;
25 initial_vma.size = address_space_end;
26 vma_map.emplace(initial_vma.base, initial_vma);
27
28 UpdatePageTableForVMA(initial_vma);
17} 29}
18 30
19GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { 31GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
20 const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, align, PageStatus::Unmapped)}; 32 const u64 aligned_size{Common::AlignUp(size, page_size)};
33 const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
21 34
22 ASSERT_MSG(gpu_addr, "unable to find available GPU memory"); 35 AllocateMemory(gpu_addr, 0, aligned_size);
23 36
24 for (u64 offset{}; offset < size; offset += PAGE_SIZE) { 37 return gpu_addr;
25 VAddr& slot{PageSlot(*gpu_addr + offset)}; 38}
26 39
27 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 40GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) {
41 const u64 aligned_size{Common::AlignUp(size, page_size)};
28 42
29 slot = static_cast<u64>(PageStatus::Allocated); 43 AllocateMemory(gpu_addr, 0, aligned_size);
30 }
31 44
32 return *gpu_addr; 45 return gpu_addr;
33} 46}
34 47
35GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) { 48GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
36 for (u64 offset{}; offset < size; offset += PAGE_SIZE) { 49 const u64 aligned_size{Common::AlignUp(size, page_size)};
37 VAddr& slot{PageSlot(gpu_addr + offset)}; 50 const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
38
39 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped));
40 51
41 slot = static_cast<u64>(PageStatus::Allocated); 52 MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
42 }
43 53
44 return gpu_addr; 54 return gpu_addr;
45} 55}
46 56
47GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { 57GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) {
48 const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, PAGE_SIZE, PageStatus::Unmapped)}; 58 ASSERT((gpu_addr & page_mask) == 0);
59
60 const u64 aligned_size{Common::AlignUp(size, page_size)};
49 61
50 ASSERT_MSG(gpu_addr, "unable to find available GPU memory"); 62 MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
51 63
52 for (u64 offset{}; offset < size; offset += PAGE_SIZE) { 64 return gpu_addr;
53 VAddr& slot{PageSlot(*gpu_addr + offset)}; 65}
54 66
55 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 67GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
68 ASSERT((gpu_addr & page_mask) == 0);
56 69
57 slot = cpu_addr + offset; 70 const u64 aligned_size{Common::AlignUp(size, page_size)};
58 } 71 const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))};
59 72
60 const MappedRegion region{cpu_addr, *gpu_addr, size}; 73 Core::System::GetInstance().Renderer().Rasterizer().FlushAndInvalidateRegion(cache_addr,
61 mapped_regions.push_back(region); 74 aligned_size);
75 UnmapRange(gpu_addr, aligned_size);
62 76
63 return *gpu_addr; 77 return gpu_addr;
64} 78}
65 79
66GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) { 80GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) const {
67 ASSERT((gpu_addr & PAGE_MASK) == 0); 81 // Find the first Free VMA.
82 const VMAHandle vma_handle{
83 std::find_if(vma_map.begin(), vma_map.end(), [region_start, size](const auto& vma) {
84 if (vma.second.type != VirtualMemoryArea::Type::Unmapped) {
85 return false;
86 }
68 87
69 if (PageSlot(gpu_addr) != static_cast<u64>(PageStatus::Allocated)) { 88 const VAddr vma_end{vma.second.base + vma.second.size};
70 // Page has been already mapped. In this case, we must find a new area of memory to use that 89 return vma_end > region_start && vma_end >= region_start + size;
71 // is different than the specified one. Super Mario Odyssey hits this scenario when changing 90 })};
72 // areas, but we do not want to overwrite the old pages.
73 // TODO(bunnei): We need to write a hardware test to confirm this behavior.
74 91
75 LOG_ERROR(HW_GPU, "attempting to map addr 0x{:016X}, which is not available!", gpu_addr); 92 if (vma_handle == vma_map.end()) {
93 return {};
94 }
76 95
77 const std::optional<GPUVAddr> new_gpu_addr{ 96 return std::max(region_start, vma_handle->second.base);
78 FindFreeBlock(gpu_addr, size, PAGE_SIZE, PageStatus::Allocated)}; 97}
79 98
80 ASSERT_MSG(new_gpu_addr, "unable to find available GPU memory"); 99bool MemoryManager::IsAddressValid(GPUVAddr addr) const {
100 return (addr >> page_bits) < page_table.pointers.size();
101}
81 102
82 gpu_addr = *new_gpu_addr; 103std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) const {
104 if (!IsAddressValid(addr)) {
105 return {};
83 } 106 }
84 107
85 for (u64 offset{}; offset < size; offset += PAGE_SIZE) { 108 const VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]};
86 VAddr& slot{PageSlot(gpu_addr + offset)}; 109 if (cpu_addr) {
110 return cpu_addr + (addr & page_mask);
111 }
87 112
88 ASSERT(slot == static_cast<u64>(PageStatus::Allocated)); 113 return {};
114}
89 115
90 slot = cpu_addr + offset; 116template <typename T>
117T MemoryManager::Read(GPUVAddr addr) const {
118 if (!IsAddressValid(addr)) {
119 return {};
91 } 120 }
92 121
93 const MappedRegion region{cpu_addr, gpu_addr, size}; 122 const u8* page_pointer{page_table.pointers[addr >> page_bits]};
94 mapped_regions.push_back(region); 123 if (page_pointer) {
124 // NOTE: Avoid adding any extra logic to this fast-path block
125 T value;
126 std::memcpy(&value, &page_pointer[addr & page_mask], sizeof(T));
127 return value;
128 }
95 129
96 return gpu_addr; 130 switch (page_table.attributes[addr >> page_bits]) {
131 case Common::PageType::Unmapped:
132 LOG_ERROR(HW_GPU, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, addr);
133 return 0;
134 case Common::PageType::Memory:
135 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", addr);
136 break;
137 default:
138 UNREACHABLE();
139 }
140 return {};
97} 141}
98 142
99GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { 143template <typename T>
100 ASSERT((gpu_addr & PAGE_MASK) == 0); 144void MemoryManager::Write(GPUVAddr addr, T data) {
145 if (!IsAddressValid(addr)) {
146 return;
147 }
101 148
102 for (u64 offset{}; offset < size; offset += PAGE_SIZE) { 149 u8* page_pointer{page_table.pointers[addr >> page_bits]};
103 VAddr& slot{PageSlot(gpu_addr + offset)}; 150 if (page_pointer) {
151 // NOTE: Avoid adding any extra logic to this fast-path block
152 std::memcpy(&page_pointer[addr & page_mask], &data, sizeof(T));
153 return;
154 }
104 155
105 ASSERT(slot != static_cast<u64>(PageStatus::Allocated) && 156 switch (page_table.attributes[addr >> page_bits]) {
106 slot != static_cast<u64>(PageStatus::Unmapped)); 157 case Common::PageType::Unmapped:
158 LOG_ERROR(HW_GPU, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
159 static_cast<u32>(data), addr);
160 return;
161 case Common::PageType::Memory:
162 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", addr);
163 break;
164 default:
165 UNREACHABLE();
166 }
167}
107 168
108 slot = static_cast<u64>(PageStatus::Unmapped); 169template u8 MemoryManager::Read<u8>(GPUVAddr addr) const;
170template u16 MemoryManager::Read<u16>(GPUVAddr addr) const;
171template u32 MemoryManager::Read<u32>(GPUVAddr addr) const;
172template u64 MemoryManager::Read<u64>(GPUVAddr addr) const;
173template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data);
174template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
175template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
176template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data);
177
178u8* MemoryManager::GetPointer(GPUVAddr addr) {
179 if (!IsAddressValid(addr)) {
180 return {};
109 } 181 }
110 182
111 // Delete the region mappings that are contained within the unmapped region 183 u8* const page_pointer{page_table.pointers[addr >> page_bits]};
112 mapped_regions.erase(std::remove_if(mapped_regions.begin(), mapped_regions.end(), 184 if (page_pointer != nullptr) {
113 [&](const MappedRegion& region) { 185 return page_pointer + (addr & page_mask);
114 return region.gpu_addr <= gpu_addr && 186 }
115 region.gpu_addr + region.size < gpu_addr + size; 187
116 }), 188 LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr);
117 mapped_regions.end()); 189 return {};
118 return gpu_addr;
119} 190}
120 191
121GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const { 192const u8* MemoryManager::GetPointer(GPUVAddr addr) const {
122 for (const auto& region : mapped_regions) { 193 if (!IsAddressValid(addr)) {
123 const GPUVAddr region_end{region.gpu_addr + region.size}; 194 return {};
124 if (region_start >= region.gpu_addr && region_start < region_end) { 195 }
125 return region_end; 196
126 } 197 const u8* const page_pointer{page_table.pointers[addr >> page_bits]};
198 if (page_pointer != nullptr) {
199 return page_pointer + (addr & page_mask);
127 } 200 }
201
202 LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr);
128 return {}; 203 return {};
129} 204}
130 205
131std::optional<GPUVAddr> MemoryManager::FindFreeBlock(GPUVAddr region_start, u64 size, u64 align, 206void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const {
132 PageStatus status) { 207 std::memcpy(dest_buffer, GetPointer(src_addr), size);
133 GPUVAddr gpu_addr{region_start}; 208}
134 u64 free_space{}; 209void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) {
135 align = (align + PAGE_MASK) & ~PAGE_MASK; 210 std::memcpy(GetPointer(dest_addr), src_buffer, size);
211}
136 212
137 while (gpu_addr + free_space < MAX_ADDRESS) { 213void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) {
138 if (PageSlot(gpu_addr + free_space) == static_cast<u64>(status)) { 214 std::memcpy(GetPointer(dest_addr), GetPointer(src_addr), size);
139 free_space += PAGE_SIZE; 215}
140 if (free_space >= size) { 216
141 return gpu_addr; 217void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type,
142 } 218 VAddr backing_addr) {
143 } else { 219 LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size,
144 gpu_addr += free_space + PAGE_SIZE; 220 (base + size) * page_size);
145 free_space = 0; 221
146 gpu_addr = Common::AlignUp(gpu_addr, align); 222 const VAddr end{base + size};
223 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
224 base + page_table.pointers.size());
225
226 std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type);
227
228 if (memory == nullptr) {
229 std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, memory);
230 std::fill(page_table.backing_addr.begin() + base, page_table.backing_addr.begin() + end,
231 backing_addr);
232 } else {
233 while (base != end) {
234 page_table.pointers[base] = memory;
235 page_table.backing_addr[base] = backing_addr;
236
237 base += 1;
238 memory += page_size;
239 backing_addr += page_size;
147 } 240 }
148 } 241 }
242}
149 243
150 return {}; 244void MemoryManager::MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr) {
245 ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size);
246 ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base);
247 MapPages(base / page_size, size / page_size, target, Common::PageType::Memory, backing_addr);
151} 248}
152 249
153std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) { 250void MemoryManager::UnmapRegion(GPUVAddr base, u64 size) {
154 const VAddr base_addr{PageSlot(gpu_addr)}; 251 ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size);
252 ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base);
253 MapPages(base / page_size, size / page_size, nullptr, Common::PageType::Unmapped);
254}
155 255
156 if (base_addr == static_cast<u64>(PageStatus::Allocated) || 256bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
157 base_addr == static_cast<u64>(PageStatus::Unmapped) || 257 ASSERT(base + size == next.base);
158 base_addr == static_cast<u64>(PageStatus::Reserved)) { 258 if (type != next.type) {
259 return {};
260 }
261 if (type == VirtualMemoryArea::Type::Allocated && (offset + size != next.offset)) {
159 return {}; 262 return {};
160 } 263 }
264 if (type == VirtualMemoryArea::Type::Mapped && backing_memory + size != next.backing_memory) {
265 return {};
266 }
267 return true;
268}
269
270MemoryManager::VMAHandle MemoryManager::FindVMA(GPUVAddr target) const {
271 if (target >= address_space_end) {
272 return vma_map.end();
273 } else {
274 return std::prev(vma_map.upper_bound(target));
275 }
276}
277
278MemoryManager::VMAIter MemoryManager::Allocate(VMAIter vma_handle) {
279 VirtualMemoryArea& vma{vma_handle->second};
280
281 vma.type = VirtualMemoryArea::Type::Allocated;
282 vma.backing_addr = 0;
283 vma.backing_memory = {};
284 UpdatePageTableForVMA(vma);
285
286 return MergeAdjacent(vma_handle);
287}
288
289MemoryManager::VMAHandle MemoryManager::AllocateMemory(GPUVAddr target, std::size_t offset,
290 u64 size) {
291
292 // This is the appropriately sized VMA that will turn into our allocation.
293 VMAIter vma_handle{CarveVMA(target, size)};
294 VirtualMemoryArea& vma{vma_handle->second};
295
296 ASSERT(vma.size == size);
297
298 vma.offset = offset;
299
300 return Allocate(vma_handle);
301}
302
303MemoryManager::VMAHandle MemoryManager::MapBackingMemory(GPUVAddr target, u8* memory, u64 size,
304 VAddr backing_addr) {
305 // This is the appropriately sized VMA that will turn into our allocation.
306 VMAIter vma_handle{CarveVMA(target, size)};
307 VirtualMemoryArea& vma{vma_handle->second};
308
309 ASSERT(vma.size == size);
310
311 vma.type = VirtualMemoryArea::Type::Mapped;
312 vma.backing_memory = memory;
313 vma.backing_addr = backing_addr;
314 UpdatePageTableForVMA(vma);
315
316 return MergeAdjacent(vma_handle);
317}
318
319void MemoryManager::UnmapRange(GPUVAddr target, u64 size) {
320 VMAIter vma{CarveVMARange(target, size)};
321 const VAddr target_end{target + size};
322 const VMAIter end{vma_map.end()};
323
324 // The comparison against the end of the range must be done using addresses since VMAs can be
325 // merged during this process, causing invalidation of the iterators.
326 while (vma != end && vma->second.base < target_end) {
327 // Unmapped ranges return to allocated state and can be reused
328 // This behavior is used by Super Mario Odyssey, Sonic Forces, and likely other games
329 vma = std::next(Allocate(vma));
330 }
331
332 ASSERT(FindVMA(target)->second.size >= size);
333}
334
335MemoryManager::VMAIter MemoryManager::StripIterConstness(const VMAHandle& iter) {
336 // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given
337 // non-const access to its container.
338 return vma_map.erase(iter, iter); // Erases an empty range of elements
339}
340
341MemoryManager::VMAIter MemoryManager::CarveVMA(GPUVAddr base, u64 size) {
342 ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size);
343 ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: 0x{:016X}", base);
344
345 VMAIter vma_handle{StripIterConstness(FindVMA(base))};
346 if (vma_handle == vma_map.end()) {
347 // Target address is outside the managed range
348 return {};
349 }
350
351 const VirtualMemoryArea& vma{vma_handle->second};
352 if (vma.type == VirtualMemoryArea::Type::Mapped) {
353 // Region is already allocated
354 return {};
355 }
356
357 const VAddr start_in_vma{base - vma.base};
358 const VAddr end_in_vma{start_in_vma + size};
161 359
162 return base_addr + (gpu_addr & PAGE_MASK); 360 ASSERT_MSG(end_in_vma <= vma.size, "region size 0x{:016X} is less than required size 0x{:016X}",
361 vma.size, end_in_vma);
362
363 if (end_in_vma < vma.size) {
364 // Split VMA at the end of the allocated region
365 SplitVMA(vma_handle, end_in_vma);
366 }
367 if (start_in_vma != 0) {
368 // Split VMA at the start of the allocated region
369 vma_handle = SplitVMA(vma_handle, start_in_vma);
370 }
371
372 return vma_handle;
163} 373}
164 374
165std::vector<GPUVAddr> MemoryManager::CpuToGpuAddress(VAddr cpu_addr) const { 375MemoryManager::VMAIter MemoryManager::CarveVMARange(GPUVAddr target, u64 size) {
166 std::vector<GPUVAddr> results; 376 ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size);
167 for (const auto& region : mapped_regions) { 377 ASSERT_MSG((target & page_mask) == 0, "non-page aligned base: 0x{:016X}", target);
168 if (cpu_addr >= region.cpu_addr && cpu_addr < (region.cpu_addr + region.size)) { 378
169 const u64 offset{cpu_addr - region.cpu_addr}; 379 const VAddr target_end{target + size};
170 results.push_back(region.gpu_addr + offset); 380 ASSERT(target_end >= target);
381 ASSERT(size > 0);
382
383 VMAIter begin_vma{StripIterConstness(FindVMA(target))};
384 const VMAIter i_end{vma_map.lower_bound(target_end)};
385 if (std::any_of(begin_vma, i_end, [](const auto& entry) {
386 return entry.second.type == VirtualMemoryArea::Type::Unmapped;
387 })) {
388 return {};
389 }
390
391 if (target != begin_vma->second.base) {
392 begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base);
393 }
394
395 VMAIter end_vma{StripIterConstness(FindVMA(target_end))};
396 if (end_vma != vma_map.end() && target_end != end_vma->second.base) {
397 end_vma = SplitVMA(end_vma, target_end - end_vma->second.base);
398 }
399
400 return begin_vma;
401}
402
403MemoryManager::VMAIter MemoryManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
404 VirtualMemoryArea& old_vma{vma_handle->second};
405 VirtualMemoryArea new_vma{old_vma}; // Make a copy of the VMA
406
407 // For now, don't allow no-op VMA splits (trying to split at a boundary) because it's probably
408 // a bug. This restriction might be removed later.
409 ASSERT(offset_in_vma < old_vma.size);
410 ASSERT(offset_in_vma > 0);
411
412 old_vma.size = offset_in_vma;
413 new_vma.base += offset_in_vma;
414 new_vma.size -= offset_in_vma;
415
416 switch (new_vma.type) {
417 case VirtualMemoryArea::Type::Unmapped:
418 break;
419 case VirtualMemoryArea::Type::Allocated:
420 new_vma.offset += offset_in_vma;
421 break;
422 case VirtualMemoryArea::Type::Mapped:
423 new_vma.backing_memory += offset_in_vma;
424 break;
425 }
426
427 ASSERT(old_vma.CanBeMergedWith(new_vma));
428
429 return vma_map.emplace_hint(std::next(vma_handle), new_vma.base, new_vma);
430}
431
432MemoryManager::VMAIter MemoryManager::MergeAdjacent(VMAIter iter) {
433 const VMAIter next_vma{std::next(iter)};
434 if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) {
435 iter->second.size += next_vma->second.size;
436 vma_map.erase(next_vma);
437 }
438
439 if (iter != vma_map.begin()) {
440 VMAIter prev_vma{std::prev(iter)};
441 if (prev_vma->second.CanBeMergedWith(iter->second)) {
442 prev_vma->second.size += iter->second.size;
443 vma_map.erase(iter);
444 iter = prev_vma;
171 } 445 }
172 } 446 }
173 return results; 447
448 return iter;
174} 449}
175 450
176VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) { 451void MemoryManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
177 auto& block{page_table[(gpu_addr >> (PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK]}; 452 switch (vma.type) {
178 if (!block) { 453 case VirtualMemoryArea::Type::Unmapped:
179 block = std::make_unique<PageBlock>(); 454 UnmapRegion(vma.base, vma.size);
180 block->fill(static_cast<VAddr>(PageStatus::Unmapped)); 455 break;
456 case VirtualMemoryArea::Type::Allocated:
457 MapMemoryRegion(vma.base, vma.size, nullptr, vma.backing_addr);
458 break;
459 case VirtualMemoryArea::Type::Mapped:
460 MapMemoryRegion(vma.base, vma.size, vma.backing_memory, vma.backing_addr);
461 break;
181 } 462 }
182 return (*block)[(gpu_addr >> PAGE_BITS) & PAGE_BLOCK_MASK];
183} 463}
184 464
185} // namespace Tegra 465} // namespace Tegra
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index fb03497ca..178e2f655 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -1,67 +1,149 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <map>
8#include <memory>
9#include <optional> 8#include <optional>
10#include <vector>
11 9
12#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/page_table.h"
13 12
14namespace Tegra { 13namespace Tegra {
15 14
16/// Virtual addresses in the GPU's memory map are 64 bit. 15/**
17using GPUVAddr = u64; 16 * Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space
17 * with homogeneous attributes across its extents. In this particular implementation each VMA is
18 * also backed by a single host memory allocation.
19 */
20struct VirtualMemoryArea {
21 enum class Type : u8 {
22 Unmapped,
23 Allocated,
24 Mapped,
25 };
26
27 /// Virtual base address of the region.
28 GPUVAddr base{};
29 /// Size of the region.
30 u64 size{};
31 /// Memory area mapping type.
32 Type type{Type::Unmapped};
33 /// CPU memory mapped address corresponding to this memory area.
34 VAddr backing_addr{};
35 /// Offset into the backing_memory the mapping starts from.
36 std::size_t offset{};
37 /// Pointer backing this VMA.
38 u8* backing_memory{};
39
40 /// Tests if this area can be merged to the right with `next`.
41 bool CanBeMergedWith(const VirtualMemoryArea& next) const;
42};
18 43
19class MemoryManager final { 44class MemoryManager final {
20public: 45public:
21 MemoryManager(); 46 MemoryManager();
22 47
23 GPUVAddr AllocateSpace(u64 size, u64 align); 48 GPUVAddr AllocateSpace(u64 size, u64 align);
24 GPUVAddr AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align); 49 GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align);
25 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); 50 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
26 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); 51 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size);
27 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); 52 GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size);
28 GPUVAddr GetRegionEnd(GPUVAddr region_start) const; 53 std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
29 std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); 54
30 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const; 55 template <typename T>
56 T Read(GPUVAddr addr) const;
57
58 template <typename T>
59 void Write(GPUVAddr addr, T data);
31 60
32 static constexpr u64 PAGE_BITS = 16; 61 u8* GetPointer(GPUVAddr addr);
33 static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; 62 const u8* GetPointer(GPUVAddr addr) const;
34 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 63
64 void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const;
65 void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size);
66 void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size);
35 67
36private: 68private:
37 enum class PageStatus : u64 { 69 using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>;
38 Unmapped = 0xFFFFFFFFFFFFFFFFULL, 70 using VMAHandle = VMAMap::const_iterator;
39 Allocated = 0xFFFFFFFFFFFFFFFEULL, 71 using VMAIter = VMAMap::iterator;
40 Reserved = 0xFFFFFFFFFFFFFFFDULL,
41 };
42 72
43 std::optional<GPUVAddr> FindFreeBlock(GPUVAddr region_start, u64 size, u64 align, 73 bool IsAddressValid(GPUVAddr addr) const;
44 PageStatus status); 74 void MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type,
45 VAddr& PageSlot(GPUVAddr gpu_addr); 75 VAddr backing_addr = 0);
46 76 void MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr);
47 static constexpr u64 MAX_ADDRESS{0x10000000000ULL}; 77 void UnmapRegion(GPUVAddr base, u64 size);
48 static constexpr u64 PAGE_TABLE_BITS{10}; 78
49 static constexpr u64 PAGE_TABLE_SIZE{1 << PAGE_TABLE_BITS}; 79 /// Finds the VMA in which the given address is included in, or `vma_map.end()`.
50 static constexpr u64 PAGE_TABLE_MASK{PAGE_TABLE_SIZE - 1}; 80 VMAHandle FindVMA(GPUVAddr target) const;
51 static constexpr u64 PAGE_BLOCK_BITS{14}; 81
52 static constexpr u64 PAGE_BLOCK_SIZE{1 << PAGE_BLOCK_BITS}; 82 VMAHandle AllocateMemory(GPUVAddr target, std::size_t offset, u64 size);
53 static constexpr u64 PAGE_BLOCK_MASK{PAGE_BLOCK_SIZE - 1}; 83
54 84 /**
55 using PageBlock = std::array<VAddr, PAGE_BLOCK_SIZE>; 85 * Maps an unmanaged host memory pointer at a given address.
56 std::array<std::unique_ptr<PageBlock>, PAGE_TABLE_SIZE> page_table{}; 86 *
57 87 * @param target The guest address to start the mapping at.
58 struct MappedRegion { 88 * @param memory The memory to be mapped.
59 VAddr cpu_addr; 89 * @param size Size of the mapping.
60 GPUVAddr gpu_addr; 90 * @param state MemoryState tag to attach to the VMA.
61 u64 size; 91 */
62 }; 92 VMAHandle MapBackingMemory(GPUVAddr target, u8* memory, u64 size, VAddr backing_addr);
93
94 /// Unmaps a range of addresses, splitting VMAs as necessary.
95 void UnmapRange(GPUVAddr target, u64 size);
96
97 /// Converts a VMAHandle to a mutable VMAIter.
98 VMAIter StripIterConstness(const VMAHandle& iter);
99
100 /// Marks as the specfied VMA as allocated.
101 VMAIter Allocate(VMAIter vma);
102
103 /**
104 * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing
105 * the appropriate error checking.
106 */
107 VMAIter CarveVMA(GPUVAddr base, u64 size);
108
109 /**
110 * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each
111 * end of the range.
112 */
113 VMAIter CarveVMARange(GPUVAddr base, u64 size);
114
115 /**
116 * Splits a VMA in two, at the specified offset.
117 * @returns the right side of the split, with the original iterator becoming the left side.
118 */
119 VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma);
120
121 /**
122 * Checks for and merges the specified VMA with adjacent ones if possible.
123 * @returns the merged VMA or the original if no merging was possible.
124 */
125 VMAIter MergeAdjacent(VMAIter vma);
126
127 /// Updates the pages corresponding to this VMA so they match the VMA's attributes.
128 void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
129
130 /// Finds a free (unmapped region) of the specified size starting at the specified address.
131 GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size) const;
132
133private:
134 static constexpr u64 page_bits{16};
135 static constexpr u64 page_size{1 << page_bits};
136 static constexpr u64 page_mask{page_size - 1};
137
138 /// Address space in bits, this is fairly arbitrary but sufficiently large.
139 static constexpr u32 address_space_width{39};
140 /// Start address for mapping, this is fairly arbitrary but must be non-zero.
141 static constexpr GPUVAddr address_space_base{0x100000};
142 /// End of address space, based on address space in bits.
143 static constexpr GPUVAddr address_space_end{1ULL << address_space_width};
63 144
64 std::vector<MappedRegion> mapped_regions; 145 Common::PageTable page_table{page_bits};
146 VMAMap vma_map;
65}; 147};
66 148
67} // namespace Tegra 149} // namespace Tegra
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
index 9692ce143..3e91cbc83 100644
--- a/src/video_core/morton.cpp
+++ b/src/video_core/morton.cpp
@@ -6,7 +6,6 @@
6#include <cstring> 6#include <cstring>
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/memory.h"
10#include "video_core/morton.h" 9#include "video_core/morton.h"
11#include "video_core/surface.h" 10#include "video_core/surface.h"
12#include "video_core/textures/decoders.h" 11#include "video_core/textures/decoders.h"
@@ -16,12 +15,12 @@ namespace VideoCore {
16using Surface::GetBytesPerPixel; 15using Surface::GetBytesPerPixel;
17using Surface::PixelFormat; 16using Surface::PixelFormat;
18 17
19using MortonCopyFn = void (*)(u32, u32, u32, u32, u32, u32, u8*, VAddr); 18using MortonCopyFn = void (*)(u32, u32, u32, u32, u32, u32, u8*, u8*);
20using ConversionArray = std::array<MortonCopyFn, Surface::MaxPixelFormat>; 19using ConversionArray = std::array<MortonCopyFn, Surface::MaxPixelFormat>;
21 20
22template <bool morton_to_linear, PixelFormat format> 21template <bool morton_to_linear, PixelFormat format>
23static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, 22static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth,
24 u32 tile_width_spacing, u8* buffer, VAddr addr) { 23 u32 tile_width_spacing, u8* buffer, u8* addr) {
25 constexpr u32 bytes_per_pixel = GetBytesPerPixel(format); 24 constexpr u32 bytes_per_pixel = GetBytesPerPixel(format);
26 25
27 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual 26 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
@@ -34,10 +33,10 @@ static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth
34 stride, height, depth, block_height, block_depth, 33 stride, height, depth, block_height, block_depth,
35 tile_width_spacing); 34 tile_width_spacing);
36 } else { 35 } else {
37 Tegra::Texture::CopySwizzledData( 36 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
38 (stride + tile_size_x - 1) / tile_size_x, (height + tile_size_y - 1) / tile_size_y, 37 (height + tile_size_y - 1) / tile_size_y, depth,
39 depth, bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr), buffer, false, 38 bytes_per_pixel, bytes_per_pixel, addr, buffer, false,
40 block_height, block_depth, tile_width_spacing); 39 block_height, block_depth, tile_width_spacing);
41 } 40 }
42} 41}
43 42
@@ -282,7 +281,7 @@ static u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) {
282 281
283void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride, 282void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride,
284 u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing, 283 u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing,
285 u8* buffer, VAddr addr) { 284 u8* buffer, u8* addr) {
286 GetSwizzleFunction(mode, format)(stride, block_height, height, block_depth, depth, 285 GetSwizzleFunction(mode, format)(stride, block_height, height, block_depth, depth,
287 tile_width_spacing, buffer, addr); 286 tile_width_spacing, buffer, addr);
288} 287}
diff --git a/src/video_core/morton.h b/src/video_core/morton.h
index b565204b5..ee5b45555 100644
--- a/src/video_core/morton.h
+++ b/src/video_core/morton.h
@@ -13,7 +13,7 @@ enum class MortonSwizzleMode { MortonToLinear, LinearToMorton };
13 13
14void MortonSwizzle(MortonSwizzleMode mode, VideoCore::Surface::PixelFormat format, u32 stride, 14void MortonSwizzle(MortonSwizzleMode mode, VideoCore::Surface::PixelFormat format, u32 stride,
15 u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing, 15 u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing,
16 u8* buffer, VAddr addr); 16 u8* buffer, u8* addr);
17 17
18void MortonCopyPixels128(MortonSwizzleMode mode, u32 width, u32 height, u32 bytes_per_pixel, 18void MortonCopyPixels128(MortonSwizzleMode mode, u32 width, u32 height, u32 bytes_per_pixel,
19 u32 linear_bytes_per_pixel, u8* morton_data, u8* linear_data); 19 u32 linear_bytes_per_pixel, u8* morton_data, u8* linear_data);
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index a7bcf26fb..291772186 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <mutex>
7#include <set> 8#include <set>
8#include <unordered_map> 9#include <unordered_map>
9 10
@@ -12,14 +13,26 @@
12 13
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "core/settings.h" 15#include "core/settings.h"
16#include "video_core/gpu.h"
15#include "video_core/rasterizer_interface.h" 17#include "video_core/rasterizer_interface.h"
16 18
17class RasterizerCacheObject { 19class RasterizerCacheObject {
18public: 20public:
21 explicit RasterizerCacheObject(const u8* host_ptr)
22 : host_ptr{host_ptr}, cache_addr{ToCacheAddr(host_ptr)} {}
23
19 virtual ~RasterizerCacheObject(); 24 virtual ~RasterizerCacheObject();
20 25
26 CacheAddr GetCacheAddr() const {
27 return cache_addr;
28 }
29
30 const u8* GetHostPtr() const {
31 return host_ptr;
32 }
33
21 /// Gets the address of the shader in guest memory, required for cache management 34 /// Gets the address of the shader in guest memory, required for cache management
22 virtual VAddr GetAddr() const = 0; 35 virtual VAddr GetCpuAddr() const = 0;
23 36
24 /// Gets the size of the shader in guest memory, required for cache management 37 /// Gets the size of the shader in guest memory, required for cache management
25 virtual std::size_t GetSizeInBytes() const = 0; 38 virtual std::size_t GetSizeInBytes() const = 0;
@@ -58,6 +71,8 @@ private:
58 bool is_registered{}; ///< Whether the object is currently registered with the cache 71 bool is_registered{}; ///< Whether the object is currently registered with the cache
59 bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory) 72 bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
60 u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing 73 u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
74 const u8* host_ptr{}; ///< Pointer to the memory backing this cached region
75 CacheAddr cache_addr{}; ///< Cache address memory, unique from emulated virtual address space
61}; 76};
62 77
63template <class T> 78template <class T>
@@ -68,7 +83,9 @@ public:
68 explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {} 83 explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {}
69 84
70 /// Write any cached resources overlapping the specified region back to memory 85 /// Write any cached resources overlapping the specified region back to memory
71 void FlushRegion(Tegra::GPUVAddr addr, size_t size) { 86 void FlushRegion(CacheAddr addr, std::size_t size) {
87 std::lock_guard lock{mutex};
88
72 const auto& objects{GetSortedObjectsFromRegion(addr, size)}; 89 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
73 for (auto& object : objects) { 90 for (auto& object : objects) {
74 FlushObject(object); 91 FlushObject(object);
@@ -76,7 +93,9 @@ public:
76 } 93 }
77 94
78 /// Mark the specified region as being invalidated 95 /// Mark the specified region as being invalidated
79 void InvalidateRegion(VAddr addr, u64 size) { 96 void InvalidateRegion(CacheAddr addr, u64 size) {
97 std::lock_guard lock{mutex};
98
80 const auto& objects{GetSortedObjectsFromRegion(addr, size)}; 99 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
81 for (auto& object : objects) { 100 for (auto& object : objects) {
82 if (!object->IsRegistered()) { 101 if (!object->IsRegistered()) {
@@ -89,48 +108,60 @@ public:
89 108
90 /// Invalidates everything in the cache 109 /// Invalidates everything in the cache
91 void InvalidateAll() { 110 void InvalidateAll() {
111 std::lock_guard lock{mutex};
112
92 while (interval_cache.begin() != interval_cache.end()) { 113 while (interval_cache.begin() != interval_cache.end()) {
93 Unregister(*interval_cache.begin()->second.begin()); 114 Unregister(*interval_cache.begin()->second.begin());
94 } 115 }
95 } 116 }
96 117
97protected: 118protected:
98 /// Tries to get an object from the cache with the specified address 119 /// Tries to get an object from the cache with the specified cache address
99 T TryGet(VAddr addr) const { 120 T TryGet(CacheAddr addr) const {
100 const auto iter = map_cache.find(addr); 121 const auto iter = map_cache.find(addr);
101 if (iter != map_cache.end()) 122 if (iter != map_cache.end())
102 return iter->second; 123 return iter->second;
103 return nullptr; 124 return nullptr;
104 } 125 }
105 126
127 T TryGet(const void* addr) const {
128 const auto iter = map_cache.find(ToCacheAddr(addr));
129 if (iter != map_cache.end())
130 return iter->second;
131 return nullptr;
132 }
133
106 /// Register an object into the cache 134 /// Register an object into the cache
107 void Register(const T& object) { 135 virtual void Register(const T& object) {
136 std::lock_guard lock{mutex};
137
108 object->SetIsRegistered(true); 138 object->SetIsRegistered(true);
109 interval_cache.add({GetInterval(object), ObjectSet{object}}); 139 interval_cache.add({GetInterval(object), ObjectSet{object}});
110 map_cache.insert({object->GetAddr(), object}); 140 map_cache.insert({object->GetCacheAddr(), object});
111 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); 141 rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), 1);
112 } 142 }
113 143
114 /// Unregisters an object from the cache 144 /// Unregisters an object from the cache
115 void Unregister(const T& object) { 145 virtual void Unregister(const T& object) {
116 object->SetIsRegistered(false); 146 std::lock_guard lock{mutex};
117 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
118 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
119 if (Settings::values.use_accurate_gpu_emulation) {
120 FlushObject(object);
121 }
122 147
148 object->SetIsRegistered(false);
149 rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1);
123 interval_cache.subtract({GetInterval(object), ObjectSet{object}}); 150 interval_cache.subtract({GetInterval(object), ObjectSet{object}});
124 map_cache.erase(object->GetAddr()); 151 map_cache.erase(object->GetCacheAddr());
125 } 152 }
126 153
127 /// Returns a ticks counter used for tracking when cached objects were last modified 154 /// Returns a ticks counter used for tracking when cached objects were last modified
128 u64 GetModifiedTicks() { 155 u64 GetModifiedTicks() {
156 std::lock_guard lock{mutex};
157
129 return ++modified_ticks; 158 return ++modified_ticks;
130 } 159 }
131 160
132 /// Flushes the specified object, updating appropriate cache state as needed 161 /// Flushes the specified object, updating appropriate cache state as needed
133 void FlushObject(const T& object) { 162 void FlushObject(const T& object) {
163 std::lock_guard lock{mutex};
164
134 if (!object->IsDirty()) { 165 if (!object->IsDirty()) {
135 return; 166 return;
136 } 167 }
@@ -140,7 +171,7 @@ protected:
140 171
141private: 172private:
142 /// Returns a list of cached objects from the specified memory region, ordered by access time 173 /// Returns a list of cached objects from the specified memory region, ordered by access time
143 std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) { 174 std::vector<T> GetSortedObjectsFromRegion(CacheAddr addr, u64 size) {
144 if (size == 0) { 175 if (size == 0) {
145 return {}; 176 return {};
146 } 177 }
@@ -164,17 +195,18 @@ private:
164 } 195 }
165 196
166 using ObjectSet = std::set<T>; 197 using ObjectSet = std::set<T>;
167 using ObjectCache = std::unordered_map<VAddr, T>; 198 using ObjectCache = std::unordered_map<CacheAddr, T>;
168 using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>; 199 using IntervalCache = boost::icl::interval_map<CacheAddr, ObjectSet>;
169 using ObjectInterval = typename IntervalCache::interval_type; 200 using ObjectInterval = typename IntervalCache::interval_type;
170 201
171 static auto GetInterval(const T& object) { 202 static auto GetInterval(const T& object) {
172 return ObjectInterval::right_open(object->GetAddr(), 203 return ObjectInterval::right_open(object->GetCacheAddr(),
173 object->GetAddr() + object->GetSizeInBytes()); 204 object->GetCacheAddr() + object->GetSizeInBytes());
174 } 205 }
175 206
176 ObjectCache map_cache; 207 ObjectCache map_cache;
177 IntervalCache interval_cache; ///< Cache of objects 208 IntervalCache interval_cache; ///< Cache of objects
178 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing 209 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
179 VideoCore::RasterizerInterface& rasterizer; 210 VideoCore::RasterizerInterface& rasterizer;
211 std::recursive_mutex mutex;
180}; 212};
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 6a1dc9cf6..d7b86df38 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -9,7 +9,6 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/engines/fermi_2d.h" 10#include "video_core/engines/fermi_2d.h"
11#include "video_core/gpu.h" 11#include "video_core/gpu.h"
12#include "video_core/memory_manager.h"
13 12
14namespace VideoCore { 13namespace VideoCore {
15 14
@@ -35,14 +34,14 @@ public:
35 virtual void FlushAll() = 0; 34 virtual void FlushAll() = 0;
36 35
37 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 36 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
38 virtual void FlushRegion(VAddr addr, u64 size) = 0; 37 virtual void FlushRegion(CacheAddr addr, u64 size) = 0;
39 38
40 /// Notify rasterizer that any caches of the specified region should be invalidated 39 /// Notify rasterizer that any caches of the specified region should be invalidated
41 virtual void InvalidateRegion(VAddr addr, u64 size) = 0; 40 virtual void InvalidateRegion(CacheAddr addr, u64 size) = 0;
42 41
43 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 42 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
44 /// and invalidated 43 /// and invalidated
45 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 44 virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0;
46 45
47 /// Attempt to use a faster method to perform a surface copy 46 /// Attempt to use a faster method to perform a surface copy
48 virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, 47 virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
@@ -63,7 +62,7 @@ public:
63 } 62 }
64 63
65 /// Increase/decrease the number of object in pages touching the specified region 64 /// Increase/decrease the number of object in pages touching the specified region
66 virtual void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) {} 65 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {}
67 66
68 /// Initialize disk cached resources for the game being emulated 67 /// Initialize disk cached resources for the game being emulated
69 virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, 68 virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index b3062e5ba..7989ec11b 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -7,30 +7,33 @@
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"
11#include "video_core/renderer_opengl/gl_buffer_cache.h" 10#include "video_core/renderer_opengl/gl_buffer_cache.h"
12#include "video_core/renderer_opengl/gl_rasterizer.h" 11#include "video_core/renderer_opengl/gl_rasterizer.h"
13 12
14namespace OpenGL { 13namespace OpenGL {
15 14
15CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, GLintptr offset,
16 std::size_t alignment, u8* host_ptr)
17 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size}, offset{offset},
18 alignment{alignment} {}
19
16OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size) 20OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
17 : RasterizerCache{rasterizer}, stream_buffer(size, true) {} 21 : RasterizerCache{rasterizer}, stream_buffer(size, true) {}
18 22
19GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, 23GLintptr OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment,
20 std::size_t alignment, bool cache) { 24 bool cache) {
21 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 25 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
22 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
23 ASSERT_MSG(cpu_addr, "Invalid GPU address");
24 26
25 // Cache management is a big overhead, so only cache entries with a given size. 27 // Cache management is a big overhead, so only cache entries with a given size.
26 // TODO: Figure out which size is the best for given games. 28 // TODO: Figure out which size is the best for given games.
27 cache &= size >= 2048; 29 cache &= size >= 2048;
28 30
31 const auto& host_ptr{memory_manager.GetPointer(gpu_addr)};
29 if (cache) { 32 if (cache) {
30 auto entry = TryGet(*cpu_addr); 33 auto entry = TryGet(host_ptr);
31 if (entry) { 34 if (entry) {
32 if (entry->size >= size && entry->alignment == alignment) { 35 if (entry->GetSize() >= size && entry->GetAlignment() == alignment) {
33 return entry->offset; 36 return entry->GetOffset();
34 } 37 }
35 Unregister(entry); 38 Unregister(entry);
36 } 39 }
@@ -39,17 +42,17 @@ GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size
39 AlignBuffer(alignment); 42 AlignBuffer(alignment);
40 const GLintptr uploaded_offset = buffer_offset; 43 const GLintptr uploaded_offset = buffer_offset;
41 44
42 Memory::ReadBlock(*cpu_addr, buffer_ptr, size); 45 if (!host_ptr) {
46 return uploaded_offset;
47 }
43 48
49 std::memcpy(buffer_ptr, host_ptr, size);
44 buffer_ptr += size; 50 buffer_ptr += size;
45 buffer_offset += size; 51 buffer_offset += size;
46 52
47 if (cache) { 53 if (cache) {
48 auto entry = std::make_shared<CachedBufferEntry>(); 54 auto entry = std::make_shared<CachedBufferEntry>(
49 entry->offset = uploaded_offset; 55 *memory_manager.GpuToCpuAddress(gpu_addr), size, uploaded_offset, alignment, host_ptr);
50 entry->size = size;
51 entry->alignment = alignment;
52 entry->addr = *cpu_addr;
53 Register(entry); 56 Register(entry);
54 } 57 }
55 58
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index c11acfb79..fc33aa433 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -17,22 +17,39 @@ namespace OpenGL {
17 17
18class RasterizerOpenGL; 18class RasterizerOpenGL;
19 19
20struct CachedBufferEntry final : public RasterizerCacheObject { 20class CachedBufferEntry final : public RasterizerCacheObject {
21 VAddr GetAddr() const override { 21public:
22 return addr; 22 explicit CachedBufferEntry(VAddr cpu_addr, std::size_t size, GLintptr offset,
23 std::size_t alignment, u8* host_ptr);
24
25 VAddr GetCpuAddr() const override {
26 return cpu_addr;
23 } 27 }
24 28
25 std::size_t GetSizeInBytes() const override { 29 std::size_t GetSizeInBytes() const override {
26 return size; 30 return size;
27 } 31 }
28 32
33 std::size_t GetSize() const {
34 return size;
35 }
36
37 GLintptr GetOffset() const {
38 return offset;
39 }
40
41 std::size_t GetAlignment() const {
42 return alignment;
43 }
44
29 // We do not have to flush this cache as things in it are never modified by us. 45 // We do not have to flush this cache as things in it are never modified by us.
30 void Flush() override {} 46 void Flush() override {}
31 47
32 VAddr addr; 48private:
33 std::size_t size; 49 VAddr cpu_addr{};
34 GLintptr offset; 50 std::size_t size{};
35 std::size_t alignment; 51 GLintptr offset{};
52 std::size_t alignment{};
36}; 53};
37 54
38class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { 55class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
@@ -41,7 +58,7 @@ public:
41 58
42 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been 59 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been
43 /// allocated. 60 /// allocated.
44 GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, 61 GLintptr UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4,
45 bool cache = true); 62 bool cache = true);
46 63
47 /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. 64 /// Uploads from a host memory. Returns host's buffer offset where it's been allocated.
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp
index 7161d1dea..5842d6213 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_global_cache.cpp
@@ -4,10 +4,8 @@
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"
10#include "core/memory.h"
11#include "video_core/renderer_opengl/gl_global_cache.h" 9#include "video_core/renderer_opengl/gl_global_cache.h"
12#include "video_core/renderer_opengl/gl_rasterizer.h" 10#include "video_core/renderer_opengl/gl_rasterizer.h"
13#include "video_core/renderer_opengl/gl_shader_decompiler.h" 11#include "video_core/renderer_opengl/gl_shader_decompiler.h"
@@ -15,12 +13,13 @@
15 13
16namespace OpenGL { 14namespace OpenGL {
17 15
18CachedGlobalRegion::CachedGlobalRegion(VAddr addr, u32 size) : addr{addr}, size{size} { 16CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr)
17 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size} {
19 buffer.Create(); 18 buffer.Create();
20 // Bind and unbind the buffer so it gets allocated by the driver 19 // Bind and unbind the buffer so it gets allocated by the driver
21 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); 20 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
22 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 21 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
23 LabelGLObject(GL_BUFFER, buffer.handle, addr, "GlobalMemory"); 22 LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory");
24} 23}
25 24
26void CachedGlobalRegion::Reload(u32 size_) { 25void CachedGlobalRegion::Reload(u32 size_) {
@@ -35,10 +34,10 @@ void CachedGlobalRegion::Reload(u32 size_) {
35 34
36 // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer 35 // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer
37 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); 36 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
38 glBufferData(GL_SHADER_STORAGE_BUFFER, size, Memory::GetPointer(addr), GL_DYNAMIC_DRAW); 37 glBufferData(GL_SHADER_STORAGE_BUFFER, size, GetHostPtr(), GL_DYNAMIC_DRAW);
39} 38}
40 39
41GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32 size) const { 40GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const {
42 const auto search{reserve.find(addr)}; 41 const auto search{reserve.find(addr)};
43 if (search == reserve.end()) { 42 if (search == reserve.end()) {
44 return {}; 43 return {};
@@ -46,11 +45,14 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32
46 return search->second; 45 return search->second;
47} 46}
48 47
49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 size) { 48GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u32 size,
50 GlobalRegion region{TryGetReservedGlobalRegion(addr, size)}; 49 u8* host_ptr) {
50 GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)};
51 if (!region) { 51 if (!region) {
52 // No reserved surface available, create a new one and reserve it 52 // No reserved surface available, create a new one and reserve it
53 region = std::make_shared<CachedGlobalRegion>(addr, size); 53 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
54 const auto cpu_addr = *memory_manager.GpuToCpuAddress(addr);
55 region = std::make_shared<CachedGlobalRegion>(cpu_addr, size, host_ptr);
54 ReserveGlobalRegion(region); 56 ReserveGlobalRegion(region);
55 } 57 }
56 region->Reload(size); 58 region->Reload(size);
@@ -58,7 +60,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 si
58} 60}
59 61
60void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) { 62void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) {
61 reserve.insert_or_assign(region->GetAddr(), std::move(region)); 63 reserve.insert_or_assign(region->GetCacheAddr(), std::move(region));
62} 64}
63 65
64GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer) 66GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
@@ -69,22 +71,20 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
69 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) { 71 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) {
70 72
71 auto& gpu{Core::System::GetInstance().GPU()}; 73 auto& gpu{Core::System::GetInstance().GPU()};
72 const auto cbufs = gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]; 74 auto& memory_manager{gpu.MemoryManager()};
73 const auto cbuf_addr = gpu.MemoryManager().GpuToCpuAddress( 75 const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]};
74 cbufs.const_buffers[global_region.GetCbufIndex()].address + global_region.GetCbufOffset()); 76 const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address +
75 ASSERT(cbuf_addr); 77 global_region.GetCbufOffset()};
76 78 const auto actual_addr{memory_manager.Read<u64>(addr)};
77 const auto actual_addr_gpu = Memory::Read64(*cbuf_addr); 79 const auto size{memory_manager.Read<u32>(addr + 8)};
78 const auto size = Memory::Read32(*cbuf_addr + 8);
79 const auto actual_addr = gpu.MemoryManager().GpuToCpuAddress(actual_addr_gpu);
80 ASSERT(actual_addr);
81 80
82 // Look up global region in the cache based on address 81 // Look up global region in the cache based on address
83 GlobalRegion region = TryGet(*actual_addr); 82 const auto& host_ptr{memory_manager.GetPointer(actual_addr)};
83 GlobalRegion region{TryGet(host_ptr)};
84 84
85 if (!region) { 85 if (!region) {
86 // No global region found - create a new one 86 // No global region found - create a new one
87 region = GetUncachedGlobalRegion(*actual_addr, size); 87 region = GetUncachedGlobalRegion(actual_addr, size, host_ptr);
88 Register(region); 88 Register(region);
89 } 89 }
90 90
diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h
index ba2bdc60c..5a21ab66f 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.h
+++ b/src/video_core/renderer_opengl/gl_global_cache.h
@@ -27,14 +27,12 @@ using GlobalRegion = std::shared_ptr<CachedGlobalRegion>;
27 27
28class CachedGlobalRegion final : public RasterizerCacheObject { 28class CachedGlobalRegion final : public RasterizerCacheObject {
29public: 29public:
30 explicit CachedGlobalRegion(VAddr addr, u32 size); 30 explicit CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr);
31 31
32 /// Gets the address of the shader in guest memory, required for cache management 32 VAddr GetCpuAddr() const override {
33 VAddr GetAddr() const override { 33 return cpu_addr;
34 return addr;
35 } 34 }
36 35
37 /// Gets the size of the shader in guest memory, required for cache management
38 std::size_t GetSizeInBytes() const override { 36 std::size_t GetSizeInBytes() const override {
39 return size; 37 return size;
40 } 38 }
@@ -53,9 +51,8 @@ public:
53 } 51 }
54 52
55private: 53private:
56 VAddr addr{}; 54 VAddr cpu_addr{};
57 u32 size{}; 55 u32 size{};
58
59 OGLBuffer buffer; 56 OGLBuffer buffer;
60}; 57};
61 58
@@ -68,11 +65,11 @@ public:
68 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage); 65 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage);
69 66
70private: 67private:
71 GlobalRegion TryGetReservedGlobalRegion(VAddr addr, u32 size) const; 68 GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const;
72 GlobalRegion GetUncachedGlobalRegion(VAddr addr, u32 size); 69 GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u32 size, u8* host_ptr);
73 void ReserveGlobalRegion(GlobalRegion region); 70 void ReserveGlobalRegion(GlobalRegion region);
74 71
75 std::unordered_map<VAddr, GlobalRegion> reserve; 72 std::unordered_map<CacheAddr, GlobalRegion> reserve;
76}; 73};
77 74
78} // namespace OpenGL 75} // 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 77d5cedd2..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
@@ -40,16 +40,12 @@ GLintptr PrimitiveAssembler::MakeQuadArray(u32 first, u32 count) {
40 return index_offset; 40 return index_offset;
41} 41}
42 42
43GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, 43GLintptr PrimitiveAssembler::MakeQuadIndexed(GPUVAddr gpu_addr, std::size_t index_size, u32 count) {
44 u32 count) {
45 const std::size_t map_size{CalculateQuadSize(count)}; 44 const std::size_t map_size{CalculateQuadSize(count)};
46 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); 45 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size);
47 46
48 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 47 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
49 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; 48 const u8* source{memory_manager.GetPointer(gpu_addr)};
50 ASSERT_MSG(cpu_addr, "Invalid GPU address");
51
52 const u8* source{Memory::GetPointer(*cpu_addr)};
53 49
54 for (u32 primitive = 0; primitive < count / 4; ++primitive) { 50 for (u32 primitive = 0; primitive < count / 4; ++primitive) {
55 for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) { 51 for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) {
@@ -64,4 +60,4 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size
64 return index_offset; 60 return index_offset;
65} 61}
66 62
67} // namespace OpenGL \ No newline at end of file 63} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.h b/src/video_core/renderer_opengl/gl_primitive_assembler.h
index a8cb88eb5..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
@@ -24,7 +22,7 @@ public:
24 22
25 GLintptr MakeQuadArray(u32 first, u32 count); 23 GLintptr MakeQuadArray(u32 first, u32 count);
26 24
27 GLintptr MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, u32 count); 25 GLintptr MakeQuadIndexed(GPUVAddr gpu_addr, std::size_t index_size, u32 count);
28 26
29private: 27private:
30 OGLBufferCache& buffer_cache; 28 OGLBufferCache& buffer_cache;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 976f64c24..7ff1e6737 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
@@ -100,11 +98,9 @@ struct FramebufferCacheKey {
100 } 98 }
101}; 99};
102 100
103RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system, 101RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info)
104 ScreenInfo& info) 102 : res_cache{*this}, shader_cache{*this, system}, global_cache{*this}, system{system},
105 : res_cache{*this}, shader_cache{*this, system}, global_cache{*this}, 103 screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) {
106 emu_window{window}, system{system}, screen_info{info},
107 buffer_cache(*this, STREAM_BUFFER_SIZE) {
108 // Create sampler objects 104 // Create sampler objects
109 for (std::size_t i = 0; i < texture_samplers.size(); ++i) { 105 for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
110 texture_samplers[i].Create(); 106 texture_samplers[i].Create();
@@ -225,8 +221,8 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
225 if (!vertex_array.IsEnabled()) 221 if (!vertex_array.IsEnabled())
226 continue; 222 continue;
227 223
228 const Tegra::GPUVAddr start = vertex_array.StartAddress(); 224 const GPUVAddr start = vertex_array.StartAddress();
229 const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); 225 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
230 226
231 ASSERT(end > start); 227 ASSERT(end > start);
232 const u64 size = end - start + 1; 228 const u64 size = end - start + 1;
@@ -320,7 +316,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
320 const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 316 const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
321 317
322 GLShader::MaxwellUniformData ubo{}; 318 GLShader::MaxwellUniformData ubo{};
323 ubo.SetFromRegs(gpu.state.shader_stages[stage]); 319 ubo.SetFromRegs(gpu, stage);
324 const GLintptr offset = buffer_cache.UploadHostMemory( 320 const GLintptr offset = buffer_cache.UploadHostMemory(
325 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); 321 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
326 322
@@ -421,8 +417,8 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
421 if (!regs.vertex_array[index].IsEnabled()) 417 if (!regs.vertex_array[index].IsEnabled())
422 continue; 418 continue;
423 419
424 const Tegra::GPUVAddr start = regs.vertex_array[index].StartAddress(); 420 const GPUVAddr start = regs.vertex_array[index].StartAddress();
425 const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); 421 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
426 422
427 ASSERT(end > start); 423 ASSERT(end > start);
428 size += end - start + 1; 424 size += end - start + 1;
@@ -449,7 +445,7 @@ static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
449 return boost::make_iterator_range(map.equal_range(interval)); 445 return boost::make_iterator_range(map.equal_range(interval));
450} 446}
451 447
452void RasterizerOpenGL::UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) { 448void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
453 const u64 page_start{addr >> Memory::PAGE_BITS}; 449 const u64 page_start{addr >> Memory::PAGE_BITS};
454 const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS}; 450 const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS};
455 451
@@ -747,20 +743,26 @@ void RasterizerOpenGL::DrawArrays() {
747 743
748void RasterizerOpenGL::FlushAll() {} 744void RasterizerOpenGL::FlushAll() {}
749 745
750void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { 746void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) {
751 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 747 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
748 if (!addr || !size) {
749 return;
750 }
752 res_cache.FlushRegion(addr, size); 751 res_cache.FlushRegion(addr, size);
753} 752}
754 753
755void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 754void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) {
756 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 755 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
756 if (!addr || !size) {
757 return;
758 }
757 res_cache.InvalidateRegion(addr, size); 759 res_cache.InvalidateRegion(addr, size);
758 shader_cache.InvalidateRegion(addr, size); 760 shader_cache.InvalidateRegion(addr, size);
759 global_cache.InvalidateRegion(addr, size); 761 global_cache.InvalidateRegion(addr, size);
760 buffer_cache.InvalidateRegion(addr, size); 762 buffer_cache.InvalidateRegion(addr, size);
761} 763}
762 764
763void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 765void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
764 FlushRegion(addr, size); 766 FlushRegion(addr, size);
765 InvalidateRegion(addr, size); 767 InvalidateRegion(addr, size);
766} 768}
@@ -782,7 +784,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
782 784
783 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 785 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
784 786
785 const auto& surface{res_cache.TryFindFramebufferSurface(framebuffer_addr)}; 787 const auto& surface{res_cache.TryFindFramebufferSurface(Memory::GetPointer(framebuffer_addr))};
786 if (!surface) { 788 if (!surface) {
787 return {}; 789 return {};
788 } 790 }
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index ca3de0592..54fbf48aa 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,8 @@
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"
36 31
37namespace Core { 32namespace Core {
38class System; 33class System;
@@ -50,16 +45,15 @@ struct FramebufferCacheKey;
50 45
51class RasterizerOpenGL : public VideoCore::RasterizerInterface { 46class RasterizerOpenGL : public VideoCore::RasterizerInterface {
52public: 47public:
53 explicit RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system, 48 explicit RasterizerOpenGL(Core::System& system, ScreenInfo& info);
54 ScreenInfo& info);
55 ~RasterizerOpenGL() override; 49 ~RasterizerOpenGL() override;
56 50
57 void DrawArrays() override; 51 void DrawArrays() override;
58 void Clear() override; 52 void Clear() override;
59 void FlushAll() override; 53 void FlushAll() override;
60 void FlushRegion(VAddr addr, u64 size) override; 54 void FlushRegion(CacheAddr addr, u64 size) override;
61 void InvalidateRegion(VAddr addr, u64 size) override; 55 void InvalidateRegion(CacheAddr addr, u64 size) override;
62 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 56 void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
63 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, 57 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
64 const Tegra::Engines::Fermi2D::Regs::Surface& dst, 58 const Tegra::Engines::Fermi2D::Regs::Surface& dst,
65 const Common::Rectangle<u32>& src_rect, 59 const Common::Rectangle<u32>& src_rect,
@@ -67,7 +61,7 @@ public:
67 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 61 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
68 u32 pixel_stride) override; 62 u32 pixel_stride) override;
69 bool AccelerateDrawBatch(bool is_indexed) override; 63 bool AccelerateDrawBatch(bool is_indexed) override;
70 void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) override; 64 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
71 void LoadDiskResources(const std::atomic_bool& stop_loading, 65 void LoadDiskResources(const std::atomic_bool& stop_loading,
72 const VideoCore::DiskResourceLoadCallback& callback) override; 66 const VideoCore::DiskResourceLoadCallback& callback) override;
73 67
@@ -214,7 +208,6 @@ private:
214 ShaderCacheOpenGL shader_cache; 208 ShaderCacheOpenGL shader_cache;
215 GlobalRegionCacheOpenGL global_cache; 209 GlobalRegionCacheOpenGL global_cache;
216 210
217 Core::Frontend::EmuWindow& emu_window;
218 Core::System& system; 211 Core::System& system;
219 212
220 ScreenInfo& screen_info; 213 ScreenInfo& screen_info;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index bd1409660..7a3280620 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -13,7 +13,6 @@
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"
19#include "video_core/morton.h" 18#include "video_core/morton.h"
@@ -55,12 +54,11 @@ static void ApplyTextureDefaults(GLuint texture, u32 max_mip_level) {
55 } 54 }
56} 55}
57 56
58void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { 57void SurfaceParams::InitCacheParameters(GPUVAddr gpu_addr_) {
59 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; 58 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
60 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
61 59
62 addr = cpu_addr ? *cpu_addr : 0;
63 gpu_addr = gpu_addr_; 60 gpu_addr = gpu_addr_;
61 host_ptr = memory_manager.GetPointer(gpu_addr_);
64 size_in_bytes = SizeInBytesRaw(); 62 size_in_bytes = SizeInBytesRaw();
65 63
66 if (IsPixelFormatASTC(pixel_format)) { 64 if (IsPixelFormatASTC(pixel_format)) {
@@ -223,7 +221,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
223} 221}
224 222
225/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer( 223/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(
226 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format, 224 u32 zeta_width, u32 zeta_height, GPUVAddr zeta_address, Tegra::DepthFormat format,
227 u32 block_width, u32 block_height, u32 block_depth, 225 u32 block_width, u32 block_height, u32 block_depth,
228 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { 226 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
229 SurfaceParams params{}; 227 SurfaceParams params{};
@@ -446,7 +444,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
446 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), 444 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
447 params.MipBlockHeight(mip_level), params.MipHeight(mip_level), 445 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
448 params.MipBlockDepth(mip_level), 1, params.tile_width_spacing, 446 params.MipBlockDepth(mip_level), 1, params.tile_width_spacing,
449 gl_buffer.data() + offset_gl, params.addr + offset); 447 gl_buffer.data() + offset_gl, params.host_ptr + offset);
450 offset += layer_size; 448 offset += layer_size;
451 offset_gl += gl_size; 449 offset_gl += gl_size;
452 } 450 }
@@ -455,7 +453,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
455 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), 453 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
456 params.MipBlockHeight(mip_level), params.MipHeight(mip_level), 454 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
457 params.MipBlockDepth(mip_level), depth, params.tile_width_spacing, 455 params.MipBlockDepth(mip_level), depth, params.tile_width_spacing,
458 gl_buffer.data(), params.addr + offset); 456 gl_buffer.data(), params.host_ptr + offset);
459 } 457 }
460} 458}
461 459
@@ -513,9 +511,9 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
513 "reinterpretation but the texture is tiled."); 511 "reinterpretation but the texture is tiled.");
514 } 512 }
515 const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes; 513 const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
516 514 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
517 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size, 515 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
518 Memory::GetPointer(dst_params.addr + src_params.size_in_bytes)); 516 memory_manager.GetPointer(dst_params.gpu_addr + src_params.size_in_bytes));
519 } 517 }
520 518
521 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 519 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -563,8 +561,14 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
563} 561}
564 562
565CachedSurface::CachedSurface(const SurfaceParams& params) 563CachedSurface::CachedSurface(const SurfaceParams& params)
566 : params(params), gl_target(SurfaceTargetToGL(params.target)), 564 : RasterizerCacheObject{params.host_ptr}, params{params},
567 cached_size_in_bytes(params.size_in_bytes) { 565 gl_target{SurfaceTargetToGL(params.target)}, cached_size_in_bytes{params.size_in_bytes} {
566
567 const auto optional_cpu_addr{
568 Core::System::GetInstance().GPU().MemoryManager().GpuToCpuAddress(params.gpu_addr)};
569 ASSERT_MSG(optional_cpu_addr, "optional_cpu_addr is invalid");
570 cpu_addr = *optional_cpu_addr;
571
568 texture.Create(gl_target); 572 texture.Create(gl_target);
569 573
570 // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0) 574 // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0)
@@ -603,19 +607,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
603 607
604 ApplyTextureDefaults(texture.handle, params.max_mip_level); 608 ApplyTextureDefaults(texture.handle, params.max_mip_level);
605 609
606 OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, params.IdentityString()); 610 OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.gpu_addr, params.IdentityString());
607
608 // Clamp size to mapped GPU memory region
609 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
610 // R32F render buffer. We do not yet know if this is a game bug or something else, but this
611 // check is necessary to prevent flushing from overwriting unmapped memory.
612
613 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
614 const u64 max_size{memory_manager.GetRegionEnd(params.gpu_addr) - params.gpu_addr};
615 if (cached_size_in_bytes > max_size) {
616 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
617 cached_size_in_bytes = max_size;
618 }
619} 611}
620 612
621MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64)); 613MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64));
@@ -633,10 +625,9 @@ void CachedSurface::LoadGLBuffer() {
633 const u32 bpp = params.GetFormatBpp() / 8; 625 const u32 bpp = params.GetFormatBpp() / 8;
634 const u32 copy_size = params.width * bpp; 626 const u32 copy_size = params.width * bpp;
635 if (params.pitch == copy_size) { 627 if (params.pitch == copy_size) {
636 std::memcpy(gl_buffer[0].data(), Memory::GetPointer(params.addr), 628 std::memcpy(gl_buffer[0].data(), params.host_ptr, params.size_in_bytes_gl);
637 params.size_in_bytes_gl);
638 } else { 629 } else {
639 const u8* start = Memory::GetPointer(params.addr); 630 const u8* start{params.host_ptr};
640 u8* write_to = gl_buffer[0].data(); 631 u8* write_to = gl_buffer[0].data();
641 for (u32 h = params.height; h > 0; h--) { 632 for (u32 h = params.height; h > 0; h--) {
642 std::memcpy(write_to, start, copy_size); 633 std::memcpy(write_to, start, copy_size);
@@ -680,8 +671,6 @@ void CachedSurface::FlushGLBuffer() {
680 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 671 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
681 Tegra::Texture::ConvertFromHostToGuest(gl_buffer[0].data(), params.pixel_format, params.width, 672 Tegra::Texture::ConvertFromHostToGuest(gl_buffer[0].data(), params.pixel_format, params.width,
682 params.height, params.depth, true, true); 673 params.height, params.depth, true, true);
683 const u8* const texture_src_data = Memory::GetPointer(params.addr);
684 ASSERT(texture_src_data);
685 if (params.is_tiled) { 674 if (params.is_tiled) {
686 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 675 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
687 params.block_width, static_cast<u32>(params.target)); 676 params.block_width, static_cast<u32>(params.target));
@@ -691,9 +680,9 @@ void CachedSurface::FlushGLBuffer() {
691 const u32 bpp = params.GetFormatBpp() / 8; 680 const u32 bpp = params.GetFormatBpp() / 8;
692 const u32 copy_size = params.width * bpp; 681 const u32 copy_size = params.width * bpp;
693 if (params.pitch == copy_size) { 682 if (params.pitch == copy_size) {
694 std::memcpy(Memory::GetPointer(params.addr), gl_buffer[0].data(), GetSizeInBytes()); 683 std::memcpy(params.host_ptr, gl_buffer[0].data(), GetSizeInBytes());
695 } else { 684 } else {
696 u8* start = Memory::GetPointer(params.addr); 685 u8* start{params.host_ptr};
697 const u8* read_to = gl_buffer[0].data(); 686 const u8* read_to = gl_buffer[0].data();
698 for (u32 h = params.height; h > 0; h--) { 687 for (u32 h = params.height; h > 0; h--) {
699 std::memcpy(start, read_to, copy_size); 688 std::memcpy(start, read_to, copy_size);
@@ -927,12 +916,12 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
927} 916}
928 917
929Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { 918Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
930 if (params.addr == 0 || params.height * params.width == 0) { 919 if (!params.IsValid()) {
931 return {}; 920 return {};
932 } 921 }
933 922
934 // Look up surface in the cache based on address 923 // Look up surface in the cache based on address
935 Surface surface{TryGet(params.addr)}; 924 Surface surface{TryGet(params.host_ptr)};
936 if (surface) { 925 if (surface) {
937 if (surface->GetSurfaceParams().IsCompatibleSurface(params)) { 926 if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
938 // Use the cached surface as-is unless it's not synced with memory 927 // Use the cached surface as-is unless it's not synced with memory
@@ -943,7 +932,7 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
943 // If surface parameters changed and we care about keeping the previous data, recreate 932 // If surface parameters changed and we care about keeping the previous data, recreate
944 // the surface from the old one 933 // the surface from the old one
945 Surface new_surface{RecreateSurface(surface, params)}; 934 Surface new_surface{RecreateSurface(surface, params)};
946 UnregisterSurface(surface); 935 Unregister(surface);
947 Register(new_surface); 936 Register(new_surface);
948 if (new_surface->IsUploaded()) { 937 if (new_surface->IsUploaded()) {
949 RegisterReinterpretSurface(new_surface); 938 RegisterReinterpretSurface(new_surface);
@@ -951,7 +940,7 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
951 return new_surface; 940 return new_surface;
952 } else { 941 } else {
953 // Delete the old surface before creating a new one to prevent collisions. 942 // Delete the old surface before creating a new one to prevent collisions.
954 UnregisterSurface(surface); 943 Unregister(surface);
955 } 944 }
956 } 945 }
957 946
@@ -981,14 +970,16 @@ void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
981 const Surface& dst_surface) { 970 const Surface& dst_surface) {
982 const auto& init_params{src_surface->GetSurfaceParams()}; 971 const auto& init_params{src_surface->GetSurfaceParams()};
983 const auto& dst_params{dst_surface->GetSurfaceParams()}; 972 const auto& dst_params{dst_surface->GetSurfaceParams()};
984 VAddr address = init_params.addr; 973 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
985 const std::size_t layer_size = dst_params.LayerMemorySize(); 974 GPUVAddr address{init_params.gpu_addr};
975 const std::size_t layer_size{dst_params.LayerMemorySize()};
986 for (u32 layer = 0; layer < dst_params.depth; layer++) { 976 for (u32 layer = 0; layer < dst_params.depth; layer++) {
987 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) { 977 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
988 const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap); 978 const GPUVAddr sub_address{address + dst_params.GetMipmapLevelOffset(mipmap)};
989 const Surface& copy = TryGet(sub_address); 979 const Surface& copy{TryGet(memory_manager.GetPointer(sub_address))};
990 if (!copy) 980 if (!copy) {
991 continue; 981 continue;
982 }
992 const auto& src_params{copy->GetSurfaceParams()}; 983 const auto& src_params{copy->GetSurfaceParams()};
993 const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))}; 984 const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))};
994 const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))}; 985 const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))};
@@ -1163,7 +1154,8 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1163 const auto& dst_params{dst_surface->GetSurfaceParams()}; 1154 const auto& dst_params{dst_surface->GetSurfaceParams()};
1164 1155
1165 // Flush enough memory for both the source and destination surface 1156 // Flush enough memory for both the source and destination surface
1166 FlushRegion(src_params.addr, std::max(src_params.MemorySize(), dst_params.MemorySize())); 1157 FlushRegion(ToCacheAddr(src_params.host_ptr),
1158 std::max(src_params.MemorySize(), dst_params.MemorySize()));
1167 1159
1168 LoadSurface(dst_surface); 1160 LoadSurface(dst_surface);
1169} 1161}
@@ -1215,8 +1207,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1215 return new_surface; 1207 return new_surface;
1216} 1208}
1217 1209
1218Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { 1210Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(const u8* host_ptr) const {
1219 return TryGet(addr); 1211 return TryGet(host_ptr);
1220} 1212}
1221 1213
1222void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) { 1214void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) {
@@ -1243,9 +1235,9 @@ static std::optional<u32> TryFindBestMipMap(std::size_t memory, const SurfacePar
1243 return {}; 1235 return {};
1244} 1236}
1245 1237
1246static std::optional<u32> TryFindBestLayer(VAddr addr, const SurfaceParams params, u32 mipmap) { 1238static std::optional<u32> TryFindBestLayer(GPUVAddr addr, const SurfaceParams params, u32 mipmap) {
1247 const std::size_t size = params.LayerMemorySize(); 1239 const std::size_t size{params.LayerMemorySize()};
1248 VAddr start = params.addr + params.GetMipmapLevelOffset(mipmap); 1240 GPUVAddr start{params.gpu_addr + params.GetMipmapLevelOffset(mipmap)};
1249 for (u32 i = 0; i < params.depth; i++) { 1241 for (u32 i = 0; i < params.depth; i++) {
1250 if (start == addr) { 1242 if (start == addr) {
1251 return {i}; 1243 return {i};
@@ -1267,7 +1259,7 @@ static bool LayerFitReinterpretSurface(RasterizerCacheOpenGL& cache, const Surfa
1267 src_params.height == dst_params.MipHeight(*level) && 1259 src_params.height == dst_params.MipHeight(*level) &&
1268 src_params.block_height >= dst_params.MipBlockHeight(*level)) { 1260 src_params.block_height >= dst_params.MipBlockHeight(*level)) {
1269 const std::optional<u32> slot = 1261 const std::optional<u32> slot =
1270 TryFindBestLayer(render_surface->GetAddr(), dst_params, *level); 1262 TryFindBestLayer(render_surface->GetSurfaceParams().gpu_addr, dst_params, *level);
1271 if (slot.has_value()) { 1263 if (slot.has_value()) {
1272 glCopyImageSubData(render_surface->Texture().handle, 1264 glCopyImageSubData(render_surface->Texture().handle,
1273 SurfaceTargetToGL(src_params.target), 0, 0, 0, 0, 1265 SurfaceTargetToGL(src_params.target), 0, 0, 0, 0,
@@ -1283,8 +1275,8 @@ static bool LayerFitReinterpretSurface(RasterizerCacheOpenGL& cache, const Surfa
1283} 1275}
1284 1276
1285static bool IsReinterpretInvalid(const Surface render_surface, const Surface blitted_surface) { 1277static bool IsReinterpretInvalid(const Surface render_surface, const Surface blitted_surface) {
1286 const VAddr bound1 = blitted_surface->GetAddr() + blitted_surface->GetMemorySize(); 1278 const VAddr bound1 = blitted_surface->GetCpuAddr() + blitted_surface->GetMemorySize();
1287 const VAddr bound2 = render_surface->GetAddr() + render_surface->GetMemorySize(); 1279 const VAddr bound2 = render_surface->GetCpuAddr() + render_surface->GetMemorySize();
1288 if (bound2 > bound1) 1280 if (bound2 > bound1)
1289 return true; 1281 return true;
1290 const auto& dst_params = blitted_surface->GetSurfaceParams(); 1282 const auto& dst_params = blitted_surface->GetSurfaceParams();
@@ -1302,12 +1294,12 @@ static bool IsReinterpretInvalidSecond(const Surface render_surface,
1302bool RasterizerCacheOpenGL::PartialReinterpretSurface(Surface triggering_surface, 1294bool RasterizerCacheOpenGL::PartialReinterpretSurface(Surface triggering_surface,
1303 Surface intersect) { 1295 Surface intersect) {
1304 if (IsReinterpretInvalid(triggering_surface, intersect)) { 1296 if (IsReinterpretInvalid(triggering_surface, intersect)) {
1305 UnregisterSurface(intersect); 1297 Unregister(intersect);
1306 return false; 1298 return false;
1307 } 1299 }
1308 if (!LayerFitReinterpretSurface(*this, triggering_surface, intersect)) { 1300 if (!LayerFitReinterpretSurface(*this, triggering_surface, intersect)) {
1309 if (IsReinterpretInvalidSecond(triggering_surface, intersect)) { 1301 if (IsReinterpretInvalidSecond(triggering_surface, intersect)) {
1310 UnregisterSurface(intersect); 1302 Unregister(intersect);
1311 return false; 1303 return false;
1312 } 1304 }
1313 FlushObject(intersect); 1305 FlushObject(intersect);
@@ -1327,7 +1319,8 @@ void RasterizerCacheOpenGL::SignalPreDrawCall() {
1327void RasterizerCacheOpenGL::SignalPostDrawCall() { 1319void RasterizerCacheOpenGL::SignalPostDrawCall() {
1328 for (u32 i = 0; i < Maxwell::NumRenderTargets; i++) { 1320 for (u32 i = 0; i < Maxwell::NumRenderTargets; i++) {
1329 if (current_color_buffers[i] != nullptr) { 1321 if (current_color_buffers[i] != nullptr) {
1330 Surface intersect = CollideOnReinterpretedSurface(current_color_buffers[i]->GetAddr()); 1322 Surface intersect =
1323 CollideOnReinterpretedSurface(current_color_buffers[i]->GetCacheAddr());
1331 if (intersect != nullptr) { 1324 if (intersect != nullptr) {
1332 PartialReinterpretSurface(current_color_buffers[i], intersect); 1325 PartialReinterpretSurface(current_color_buffers[i], intersect);
1333 texception = true; 1326 texception = true;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 9cf6f50be..ad4fd3ad2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -5,10 +5,9 @@
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"
@@ -109,6 +108,11 @@ struct SurfaceParams {
109 return size; 108 return size;
110 } 109 }
111 110
111 /// Returns true if the parameters constitute a valid rasterizer surface.
112 bool IsValid() const {
113 return gpu_addr && host_ptr && height && width;
114 }
115
112 /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including 116 /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including
113 /// mipmaps. 117 /// mipmaps.
114 std::size_t LayerMemorySize() const { 118 std::size_t LayerMemorySize() const {
@@ -210,7 +214,7 @@ struct SurfaceParams {
210 214
211 /// Creates SurfaceParams for a depth buffer configuration 215 /// Creates SurfaceParams for a depth buffer configuration
212 static SurfaceParams CreateForDepthBuffer( 216 static SurfaceParams CreateForDepthBuffer(
213 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format, 217 u32 zeta_width, u32 zeta_height, GPUVAddr zeta_address, Tegra::DepthFormat format,
214 u32 block_width, u32 block_height, u32 block_depth, 218 u32 block_width, u32 block_height, u32 block_depth,
215 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); 219 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
216 220
@@ -232,7 +236,7 @@ struct SurfaceParams {
232 } 236 }
233 237
234 /// Initializes parameters for caching, should be called after everything has been initialized 238 /// Initializes parameters for caching, should be called after everything has been initialized
235 void InitCacheParameters(Tegra::GPUVAddr gpu_addr); 239 void InitCacheParameters(GPUVAddr gpu_addr);
236 240
237 std::string TargetName() const { 241 std::string TargetName() const {
238 switch (target) { 242 switch (target) {
@@ -296,8 +300,8 @@ struct SurfaceParams {
296 bool is_array; 300 bool is_array;
297 bool srgb_conversion; 301 bool srgb_conversion;
298 // Parameters used for caching 302 // Parameters used for caching
299 VAddr addr; 303 u8* host_ptr;
300 Tegra::GPUVAddr gpu_addr; 304 GPUVAddr gpu_addr;
301 std::size_t size_in_bytes; 305 std::size_t size_in_bytes;
302 std::size_t size_in_bytes_gl; 306 std::size_t size_in_bytes_gl;
303 307
@@ -345,10 +349,10 @@ class RasterizerOpenGL;
345 349
346class CachedSurface final : public RasterizerCacheObject { 350class CachedSurface final : public RasterizerCacheObject {
347public: 351public:
348 CachedSurface(const SurfaceParams& params); 352 explicit CachedSurface(const SurfaceParams& params);
349 353
350 VAddr GetAddr() const override { 354 VAddr GetCpuAddr() const override {
351 return params.addr; 355 return cpu_addr;
352 } 356 }
353 357
354 std::size_t GetSizeInBytes() const override { 358 std::size_t GetSizeInBytes() const override {
@@ -432,6 +436,7 @@ private:
432 std::size_t memory_size; 436 std::size_t memory_size;
433 bool reinterpreted = false; 437 bool reinterpreted = false;
434 bool must_reload = false; 438 bool must_reload = false;
439 VAddr cpu_addr{};
435}; 440};
436 441
437class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 442class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -449,7 +454,7 @@ public:
449 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); 454 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents);
450 455
451 /// Tries to find a framebuffer using on the provided CPU address 456 /// Tries to find a framebuffer using on the provided CPU address
452 Surface TryFindFramebufferSurface(VAddr addr) const; 457 Surface TryFindFramebufferSurface(const u8* host_ptr) const;
453 458
454 /// Copies the contents of one surface to another 459 /// Copies the contents of one surface to another
455 void FermiCopySurface(const Tegra::Engines::Fermi2D::Regs::Surface& src_config, 460 void FermiCopySurface(const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
@@ -506,12 +511,12 @@ private:
506 std::array<Surface, Maxwell::NumRenderTargets> current_color_buffers; 511 std::array<Surface, Maxwell::NumRenderTargets> current_color_buffers;
507 Surface last_depth_buffer; 512 Surface last_depth_buffer;
508 513
509 using SurfaceIntervalCache = boost::icl::interval_map<VAddr, Surface>; 514 using SurfaceIntervalCache = boost::icl::interval_map<CacheAddr, Surface>;
510 using SurfaceInterval = typename SurfaceIntervalCache::interval_type; 515 using SurfaceInterval = typename SurfaceIntervalCache::interval_type;
511 516
512 static auto GetReinterpretInterval(const Surface& object) { 517 static auto GetReinterpretInterval(const Surface& object) {
513 return SurfaceInterval::right_open(object->GetAddr() + 1, 518 return SurfaceInterval::right_open(object->GetCacheAddr() + 1,
514 object->GetAddr() + object->GetMemorySize() - 1); 519 object->GetCacheAddr() + object->GetMemorySize() - 1);
515 } 520 }
516 521
517 // Reinterpreted surfaces are very fragil as the game may keep rendering into them. 522 // Reinterpreted surfaces are very fragil as the game may keep rendering into them.
@@ -523,7 +528,7 @@ private:
523 reinterpret_surface->MarkReinterpreted(); 528 reinterpret_surface->MarkReinterpreted();
524 } 529 }
525 530
526 Surface CollideOnReinterpretedSurface(VAddr addr) const { 531 Surface CollideOnReinterpretedSurface(CacheAddr addr) const {
527 const SurfaceInterval interval{addr}; 532 const SurfaceInterval interval{addr};
528 for (auto& pair : 533 for (auto& pair :
529 boost::make_iterator_range(reinterpreted_surfaces.equal_range(interval))) { 534 boost::make_iterator_range(reinterpreted_surfaces.equal_range(interval))) {
@@ -532,13 +537,17 @@ private:
532 return nullptr; 537 return nullptr;
533 } 538 }
534 539
540 void Register(const Surface& object) override {
541 RasterizerCache<Surface>::Register(object);
542 }
543
535 /// Unregisters an object from the cache 544 /// Unregisters an object from the cache
536 void UnregisterSurface(const Surface& object) { 545 void Unregister(const Surface& object) override {
537 if (object->IsReinterpreted()) { 546 if (object->IsReinterpreted()) {
538 auto interval = GetReinterpretInterval(object); 547 auto interval = GetReinterpretInterval(object);
539 reinterpreted_surfaces.erase(interval); 548 reinterpreted_surfaces.erase(interval);
540 } 549 }
541 Unregister(object); 550 RasterizerCache<Surface>::Unregister(object);
542 } 551 }
543}; 552};
544 553
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 4883e4f62..7030db365 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -6,13 +6,11 @@
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"
11#include "video_core/renderer_opengl/gl_rasterizer.h" 10#include "video_core/renderer_opengl/gl_rasterizer.h"
12#include "video_core/renderer_opengl/gl_shader_cache.h" 11#include "video_core/renderer_opengl/gl_shader_cache.h"
13#include "video_core/renderer_opengl/gl_shader_decompiler.h" 12#include "video_core/renderer_opengl/gl_shader_decompiler.h"
14#include "video_core/renderer_opengl/gl_shader_disk_cache.h" 13#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" 14#include "video_core/renderer_opengl/utils.h"
17#include "video_core/shader/shader_ir.h" 15#include "video_core/shader/shader_ir.h"
18 16
@@ -32,19 +30,16 @@ struct UnspecializedShader {
32namespace { 30namespace {
33 31
34/// Gets the address for the specified shader stage program 32/// Gets the address for the specified shader stage program
35VAddr GetShaderAddress(Maxwell::ShaderProgram program) { 33GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) {
36 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 34 const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()};
37 const auto& shader_config = gpu.regs.shader_config[static_cast<std::size_t>(program)]; 35 const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
38 const auto address = gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() + 36 return gpu.regs.code_address.CodeAddress() + shader_config.offset;
39 shader_config.offset);
40 ASSERT_MSG(address, "Invalid GPU address");
41 return *address;
42} 37}
43 38
44/// Gets the shader program code from memory for the specified address 39/// Gets the shader program code from memory for the specified address
45ProgramCode GetShaderCode(VAddr addr) { 40ProgramCode GetShaderCode(const u8* host_ptr) {
46 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); 41 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
47 Memory::ReadBlock(addr, program_code.data(), program_code.size() * sizeof(u64)); 42 std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64));
48 return program_code; 43 return program_code;
49} 44}
50 45
@@ -214,12 +209,13 @@ std::set<GLenum> GetSupportedFormats() {
214 209
215} // namespace 210} // namespace
216 211
217CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 212CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
218 ShaderDiskCacheOpenGL& disk_cache, 213 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
219 const PrecompiledPrograms& precompiled_programs, 214 const PrecompiledPrograms& precompiled_programs,
220 ProgramCode&& program_code, ProgramCode&& program_code_b) 215 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr)
221 : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, 216 : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr},
222 disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { 217 unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache},
218 precompiled_programs{precompiled_programs} {
223 219
224 const std::size_t code_size = CalculateProgramSize(program_code); 220 const std::size_t code_size = CalculateProgramSize(program_code);
225 const std::size_t code_size_b = 221 const std::size_t code_size_b =
@@ -243,12 +239,13 @@ CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderPro
243 disk_cache.SaveRaw(raw); 239 disk_cache.SaveRaw(raw);
244} 240}
245 241
246CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 242CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
247 ShaderDiskCacheOpenGL& disk_cache, 243 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
248 const PrecompiledPrograms& precompiled_programs, 244 const PrecompiledPrograms& precompiled_programs,
249 GLShader::ProgramResult result) 245 GLShader::ProgramResult result, u8* host_ptr)
250 : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, 246 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier},
251 disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { 247 program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{
248 precompiled_programs} {
252 249
253 code = std::move(result.first); 250 code = std::move(result.first);
254 entries = result.second; 251 entries = result.second;
@@ -271,7 +268,7 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive
271 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); 268 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));
272 } 269 }
273 270
274 LabelGLObject(GL_PROGRAM, program->handle, addr); 271 LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
275 } 272 }
276 273
277 handle = program->handle; 274 handle = program->handle;
@@ -323,7 +320,7 @@ GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, BaseBind
323 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); 320 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));
324 } 321 }
325 322
326 LabelGLObject(GL_PROGRAM, target_program->handle, addr, debug_name); 323 LabelGLObject(GL_PROGRAM, target_program->handle, cpu_addr, debug_name);
327 324
328 return target_program->handle; 325 return target_program->handle;
329}; 326};
@@ -486,29 +483,32 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
486 return last_shaders[static_cast<u32>(program)]; 483 return last_shaders[static_cast<u32>(program)];
487 } 484 }
488 485
489 const VAddr program_addr{GetShaderAddress(program)}; 486 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
487 const GPUVAddr program_addr{GetShaderAddress(program)};
490 488
491 // Look up shader in the cache based on address 489 // Look up shader in the cache based on address
492 Shader shader{TryGet(program_addr)}; 490 const auto& host_ptr{memory_manager.GetPointer(program_addr)};
491 Shader shader{TryGet(host_ptr)};
493 492
494 if (!shader) { 493 if (!shader) {
495 // No shader found - create a new one 494 // No shader found - create a new one
496 ProgramCode program_code = GetShaderCode(program_addr); 495 ProgramCode program_code{GetShaderCode(host_ptr)};
497 ProgramCode program_code_b; 496 ProgramCode program_code_b;
498 if (program == Maxwell::ShaderProgram::VertexA) { 497 if (program == Maxwell::ShaderProgram::VertexA) {
499 program_code_b = GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB)); 498 program_code_b = GetShaderCode(
499 memory_manager.GetPointer(GetShaderAddress(Maxwell::ShaderProgram::VertexB)));
500 } 500 }
501 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); 501 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
502 502 const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
503 const auto found = precompiled_shaders.find(unique_identifier); 503 const auto found = precompiled_shaders.find(unique_identifier);
504 if (found != precompiled_shaders.end()) { 504 if (found != precompiled_shaders.end()) {
505 shader = 505 shader =
506 std::make_shared<CachedShader>(program_addr, unique_identifier, program, disk_cache, 506 std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache,
507 precompiled_programs, found->second); 507 precompiled_programs, found->second, host_ptr);
508 } else { 508 } else {
509 shader = std::make_shared<CachedShader>( 509 shader = std::make_shared<CachedShader>(
510 program_addr, unique_identifier, program, disk_cache, precompiled_programs, 510 cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
511 std::move(program_code), std::move(program_code_b)); 511 std::move(program_code), std::move(program_code_b), host_ptr);
512 } 512 }
513 Register(shader); 513 Register(shader);
514 } 514 }
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 97eed192f..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;
@@ -39,18 +38,18 @@ using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>;
39 38
40class CachedShader final : public RasterizerCacheObject { 39class CachedShader final : public RasterizerCacheObject {
41public: 40public:
42 explicit CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 41 explicit CachedShader(VAddr cpu_addr, u64 unique_identifier,
43 ShaderDiskCacheOpenGL& disk_cache, 42 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
44 const PrecompiledPrograms& precompiled_programs, 43 const PrecompiledPrograms& precompiled_programs,
45 ProgramCode&& program_code, ProgramCode&& program_code_b); 44 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr);
46 45
47 explicit CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 46 explicit CachedShader(VAddr cpu_addr, u64 unique_identifier,
48 ShaderDiskCacheOpenGL& disk_cache, 47 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
49 const PrecompiledPrograms& precompiled_programs, 48 const PrecompiledPrograms& precompiled_programs,
50 GLShader::ProgramResult result); 49 GLShader::ProgramResult result, u8* host_ptr);
51 50
52 VAddr GetAddr() const override { 51 VAddr GetCpuAddr() const override {
53 return addr; 52 return cpu_addr;
54 } 53 }
55 54
56 std::size_t GetSizeInBytes() const override { 55 std::size_t GetSizeInBytes() const override {
@@ -91,7 +90,8 @@ private:
91 90
92 ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const; 91 ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const;
93 92
94 VAddr addr{}; 93 u8* host_ptr{};
94 VAddr cpu_addr{};
95 u64 unique_identifier{}; 95 u64 unique_identifier{};
96 Maxwell::ShaderProgram program_type{}; 96 Maxwell::ShaderProgram program_type{};
97 ShaderDiskCacheOpenGL& disk_cache; 97 ShaderDiskCacheOpenGL& disk_cache;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 11d1169f0..a1a51f226 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -69,10 +69,10 @@ public:
69 shader_source += '\n'; 69 shader_source += '\n';
70 } 70 }
71 71
72 std::string GenerateTemporal() { 72 std::string GenerateTemporary() {
73 std::string temporal = "tmp"; 73 std::string temporary = "tmp";
74 temporal += std::to_string(temporal_index++); 74 temporary += std::to_string(temporary_index++);
75 return temporal; 75 return temporary;
76 } 76 }
77 77
78 std::string GetResult() { 78 std::string GetResult() {
@@ -87,7 +87,7 @@ private:
87 } 87 }
88 88
89 std::string shader_source; 89 std::string shader_source;
90 u32 temporal_index = 1; 90 u32 temporary_index = 1;
91}; 91};
92 92
93/// Generates code to use for a swizzle operation. 93/// Generates code to use for a swizzle operation.
@@ -426,9 +426,14 @@ private:
426 std::string Visit(Node node) { 426 std::string Visit(Node node) {
427 if (const auto operation = std::get_if<OperationNode>(node)) { 427 if (const auto operation = std::get_if<OperationNode>(node)) {
428 const auto operation_index = static_cast<std::size_t>(operation->GetCode()); 428 const auto operation_index = static_cast<std::size_t>(operation->GetCode());
429 if (operation_index >= operation_decompilers.size()) {
430 UNREACHABLE_MSG("Out of bounds operation: {}", operation_index);
431 return {};
432 }
429 const auto decompiler = operation_decompilers[operation_index]; 433 const auto decompiler = operation_decompilers[operation_index];
430 if (decompiler == nullptr) { 434 if (decompiler == nullptr) {
431 UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index); 435 UNREACHABLE_MSG("Undefined operation: {}", operation_index);
436 return {};
432 } 437 }
433 return (this->*decompiler)(*operation); 438 return (this->*decompiler)(*operation);
434 439
@@ -540,7 +545,7 @@ private:
540 545
541 } else if (std::holds_alternative<OperationNode>(*offset)) { 546 } else if (std::holds_alternative<OperationNode>(*offset)) {
542 // Indirect access 547 // Indirect access
543 const std::string final_offset = code.GenerateTemporal(); 548 const std::string final_offset = code.GenerateTemporary();
544 code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4) & " + 549 code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4) & " +
545 std::to_string(MAX_CONSTBUFFER_ELEMENTS - 1) + ';'); 550 std::to_string(MAX_CONSTBUFFER_ELEMENTS - 1) + ';');
546 return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), 551 return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
@@ -587,9 +592,9 @@ private:
587 // There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders 592 // 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 " : ""; 593 const std::string precise = stage != ShaderStage::Fragment ? "precise " : "";
589 594
590 const std::string temporal = code.GenerateTemporal(); 595 const std::string temporary = code.GenerateTemporary();
591 code.AddLine(precise + "float " + temporal + " = " + value + ';'); 596 code.AddLine(precise + "float " + temporary + " = " + value + ';');
592 return temporal; 597 return temporary;
593 } 598 }
594 599
595 std::string VisitOperand(Operation operation, std::size_t operand_index) { 600 std::string VisitOperand(Operation operation, std::size_t operand_index) {
@@ -601,9 +606,9 @@ private:
601 return Visit(operand); 606 return Visit(operand);
602 } 607 }
603 608
604 const std::string temporal = code.GenerateTemporal(); 609 const std::string temporary = code.GenerateTemporary();
605 code.AddLine("float " + temporal + " = " + Visit(operand) + ';'); 610 code.AddLine("float " + temporary + " = " + Visit(operand) + ';');
606 return temporal; 611 return temporary;
607 } 612 }
608 613
609 std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { 614 std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) {
@@ -1196,11 +1201,12 @@ private:
1196 switch (meta->element) { 1201 switch (meta->element) {
1197 case 0: 1202 case 0:
1198 case 1: 1203 case 1:
1199 return "textureSize(" + sampler + ", " + lod + ')' + GetSwizzle(meta->element); 1204 return "itof(int(textureSize(" + sampler + ", " + lod + ')' +
1205 GetSwizzle(meta->element) + "))";
1200 case 2: 1206 case 2:
1201 return "0"; 1207 return "0";
1202 case 3: 1208 case 3:
1203 return "textureQueryLevels(" + sampler + ')'; 1209 return "itof(textureQueryLevels(" + sampler + "))";
1204 } 1210 }
1205 UNREACHABLE(); 1211 UNREACHABLE();
1206 return "0"; 1212 return "0";
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index 72aca4938..4e04ab2f8 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>
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..d2d979997 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -4,13 +4,13 @@
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"
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "common/file_util.h" 11#include "common/file_util.h"
13#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/lz4_compression.h"
14#include "common/scm_rev.h" 14#include "common/scm_rev.h"
15 15
16#include "core/core.h" 16#include "core/core.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::DecompressDataLZ4(compressed_binary, binary_length);
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::DecompressDataLZ4(compressed_code, code_size);
325 if (code.empty()) { 292 if (code.empty()) {
326 return {}; 293 return {};
327 } 294 }
@@ -507,7 +474,8 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str
507 if (!IsUsable()) 474 if (!IsUsable())
508 return; 475 return;
509 476
510 const std::vector<u8> compressed_code{CompressData(code.data(), code.size())}; 477 const std::vector<u8> compressed_code{Common::Compression::CompressDataLZ4HC(
478 reinterpret_cast<const u8*>(code.data()), code.size(), 9)};
511 if (compressed_code.empty()) { 479 if (compressed_code.empty()) {
512 LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}", 480 LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}",
513 unique_identifier); 481 unique_identifier);
@@ -537,7 +505,9 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
537 std::vector<u8> binary(binary_length); 505 std::vector<u8> binary(binary_length);
538 glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); 506 glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data());
539 507
540 const std::vector<u8> compressed_binary = CompressData(binary.data(), binary.size()); 508 const std::vector<u8> compressed_binary =
509 Common::Compression::CompressDataLZ4HC(binary.data(), binary.size(), 9);
510
541 if (compressed_binary.empty()) { 511 if (compressed_binary.empty()) {
542 LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}", 512 LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}",
543 usage.unique_identifier); 513 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..eaf3e03a0 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -2,15 +2,15 @@
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"
6#include "video_core/renderer_opengl/gl_shader_manager.h" 5#include "video_core/renderer_opengl/gl_shader_manager.h"
7 6
8namespace OpenGL::GLShader { 7namespace OpenGL::GLShader {
9 8
10void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) { 9using Tegra::Engines::Maxwell3D;
11 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 10
12 const auto& regs = gpu.regs; 11void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) {
13 const auto& state = gpu.state; 12 const auto& regs = maxwell.regs;
13 const auto& state = maxwell.state;
14 14
15 // TODO(bunnei): Support more than one viewport 15 // TODO(bunnei): Support more than one viewport
16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; 16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
@@ -18,7 +18,7 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
18 18
19 u32 func = static_cast<u32>(regs.alpha_test_func); 19 u32 func = static_cast<u32>(regs.alpha_test_func);
20 // Normalize the gl variants of opCompare to be the same as the normal variants 20 // 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); 21 const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never);
22 if (func >= op_gl_variant_base) { 22 if (func >= op_gl_variant_base) {
23 func = func - op_gl_variant_base + 1U; 23 func = func - op_gl_variant_base + 1U;
24 } 24 }
@@ -31,8 +31,9 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
31 31
32 // Assign in which stage the position has to be flipped 32 // Assign in which stage the position has to be flipped
33 // (the last stage before the fragment shader). 33 // (the last stage before the fragment shader).
34 if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) { 34 constexpr u32 geometry_index = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
35 flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); 35 if (maxwell.regs.shader_config[geometry_index].enable) {
36 flip_stage = geometry_index;
36 } else { 37 } else {
37 flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); 38 flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
38 } 39 }
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 4970aafed..8eef2a920 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -12,14 +12,13 @@
12 12
13namespace OpenGL::GLShader { 13namespace OpenGL::GLShader {
14 14
15using Tegra::Engines::Maxwell3D;
16
17/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned 15/// 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 16/// @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. 17/// 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. 18/// Not following that rule will cause problems on some AMD drivers.
21struct MaxwellUniformData { 19struct MaxwellUniformData {
22 void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); 20 void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell, std::size_t shader_stage);
21
23 alignas(16) GLvec4 viewport_flip; 22 alignas(16) GLvec4 viewport_flip;
24 struct alignas(16) { 23 struct alignas(16) {
25 GLuint instance_id; 24 GLuint instance_id;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index b97576309..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"
@@ -164,8 +163,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
164 // Reset the screen info's display texture to its own permanent texture 163 // Reset the screen info's display texture to its own permanent texture
165 screen_info.display_texture = screen_info.texture.resource.handle; 164 screen_info.display_texture = screen_info.texture.resource.handle;
166 165
167 Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes, 166 rasterizer->FlushRegion(ToCacheAddr(Memory::GetPointer(framebuffer_addr)), size_in_bytes);
168 Memory::FlushMode::Flush);
169 167
170 constexpr u32 linear_bpp = 4; 168 constexpr u32 linear_bpp = 4;
171 VideoCore::MortonCopyPixels128(VideoCore::MortonSwizzleMode::MortonToLinear, 169 VideoCore::MortonCopyPixels128(VideoCore::MortonSwizzleMode::MortonToLinear,
@@ -267,7 +265,7 @@ void RendererOpenGL::CreateRasterizer() {
267 } 265 }
268 // Initialize sRGB Usage 266 // Initialize sRGB Usage
269 OpenGLState::ClearsRGBUsed(); 267 OpenGLState::ClearsRGBUsed();
270 rasterizer = std::make_unique<RasterizerOpenGL>(render_window, system, screen_info); 268 rasterizer = std::make_unique<RasterizerOpenGL>(system, screen_info);
271} 269}
272 270
273void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, 271void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 4a33a6c84..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"
@@ -17,6 +18,11 @@
17 18
18namespace Vulkan { 19namespace Vulkan {
19 20
21CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, u64 offset,
22 std::size_t alignment, u8* host_ptr)
23 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size}, offset{offset},
24 alignment{alignment} {}
25
20VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, 26VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager,
21 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, 27 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
22 VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size) 28 VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size)
@@ -34,19 +40,20 @@ VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager,
34 40
35VKBufferCache::~VKBufferCache() = default; 41VKBufferCache::~VKBufferCache() = default;
36 42
37u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment, 43u64 VKBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignment, bool cache) {
38 bool cache) {
39 const auto cpu_addr{tegra_memory_manager.GpuToCpuAddress(gpu_addr)}; 44 const auto cpu_addr{tegra_memory_manager.GpuToCpuAddress(gpu_addr)};
40 ASSERT(cpu_addr); 45 ASSERT_MSG(cpu_addr, "Invalid GPU address");
41 46
42 // Cache management is a big overhead, so only cache entries with a given size. 47 // Cache management is a big overhead, so only cache entries with a given size.
43 // TODO: Figure out which size is the best for given games. 48 // TODO: Figure out which size is the best for given games.
44 cache &= size >= 2048; 49 cache &= size >= 2048;
45 50
51 const auto& host_ptr{Memory::GetPointer(*cpu_addr)};
46 if (cache) { 52 if (cache) {
47 if (auto entry = TryGet(*cpu_addr); entry) { 53 auto entry = TryGet(host_ptr);
48 if (entry->size >= size && entry->alignment == alignment) { 54 if (entry) {
49 return entry->offset; 55 if (entry->GetSize() >= size && entry->GetAlignment() == alignment) {
56 return entry->GetOffset();
50 } 57 }
51 Unregister(entry); 58 Unregister(entry);
52 } 59 }
@@ -55,17 +62,17 @@ u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64
55 AlignBuffer(alignment); 62 AlignBuffer(alignment);
56 const u64 uploaded_offset = buffer_offset; 63 const u64 uploaded_offset = buffer_offset;
57 64
58 Memory::ReadBlock(*cpu_addr, buffer_ptr, size); 65 if (!host_ptr) {
66 return uploaded_offset;
67 }
59 68
69 std::memcpy(buffer_ptr, host_ptr, size);
60 buffer_ptr += size; 70 buffer_ptr += size;
61 buffer_offset += size; 71 buffer_offset += size;
62 72
63 if (cache) { 73 if (cache) {
64 auto entry = std::make_shared<CachedBufferEntry>(); 74 auto entry = std::make_shared<CachedBufferEntry>(*cpu_addr, size, uploaded_offset,
65 entry->offset = uploaded_offset; 75 alignment, host_ptr);
66 entry->size = size;
67 entry->alignment = alignment;
68 entry->addr = *cpu_addr;
69 Register(entry); 76 Register(entry);
70 } 77 }
71 78
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index d8e916f31..08b786aad 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -24,22 +24,39 @@ class VKFence;
24class VKMemoryManager; 24class VKMemoryManager;
25class VKStreamBuffer; 25class VKStreamBuffer;
26 26
27struct CachedBufferEntry final : public RasterizerCacheObject { 27class CachedBufferEntry final : public RasterizerCacheObject {
28 VAddr GetAddr() const override { 28public:
29 return addr; 29 explicit CachedBufferEntry(VAddr cpu_addr, std::size_t size, u64 offset, std::size_t alignment,
30 u8* host_ptr);
31
32 VAddr GetCpuAddr() const override {
33 return cpu_addr;
30 } 34 }
31 35
32 std::size_t GetSizeInBytes() const override { 36 std::size_t GetSizeInBytes() const override {
33 return size; 37 return size;
34 } 38 }
35 39
40 std::size_t GetSize() const {
41 return size;
42 }
43
44 u64 GetOffset() const {
45 return offset;
46 }
47
48 std::size_t GetAlignment() const {
49 return alignment;
50 }
51
36 // We do not have to flush this cache as things in it are never modified by us. 52 // We do not have to flush this cache as things in it are never modified by us.
37 void Flush() override {} 53 void Flush() override {}
38 54
39 VAddr addr; 55private:
40 std::size_t size; 56 VAddr cpu_addr{};
41 u64 offset; 57 std::size_t size{};
42 std::size_t alignment; 58 u64 offset{};
59 std::size_t alignment{};
43}; 60};
44 61
45class VKBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { 62class VKBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
@@ -51,8 +68,7 @@ public:
51 68
52 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been 69 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been
53 /// allocated. 70 /// allocated.
54 u64 UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment = 4, 71 u64 UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignment = 4, bool cache = true);
55 bool cache = true);
56 72
57 /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. 73 /// Uploads from a host memory. Returns host's buffer offset where it's been allocated.
58 u64 UploadHostMemory(const u8* raw_pointer, std::size_t size, u64 alignment = 4); 74 u64 UploadHostMemory(const u8* raw_pointer, std::size_t size, u64 alignment = 4);
diff --git a/src/video_core/renderer_vulkan/vk_resource_manager.cpp b/src/video_core/renderer_vulkan/vk_resource_manager.cpp
index a1e117443..13c46e5b8 100644
--- a/src/video_core/renderer_vulkan/vk_resource_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_resource_manager.cpp
@@ -21,7 +21,7 @@ public:
21 CommandBufferPool(const VKDevice& device) 21 CommandBufferPool(const VKDevice& device)
22 : VKFencedPool(COMMAND_BUFFER_POOL_SIZE), device{device} {} 22 : VKFencedPool(COMMAND_BUFFER_POOL_SIZE), device{device} {}
23 23
24 void Allocate(std::size_t begin, std::size_t end) { 24 void Allocate(std::size_t begin, std::size_t end) override {
25 const auto dev = device.GetLogical(); 25 const auto dev = device.GetLogical();
26 const auto& dld = device.GetDispatchLoader(); 26 const auto& dld = device.GetDispatchLoader();
27 const u32 graphics_family = device.GetGraphicsFamily(); 27 const u32 graphics_family = device.GetGraphicsFamily();
diff --git a/src/video_core/renderer_vulkan/vk_resource_manager.h b/src/video_core/renderer_vulkan/vk_resource_manager.h
index 5bfe4cead..08ee86fa6 100644
--- a/src/video_core/renderer_vulkan/vk_resource_manager.h
+++ b/src/video_core/renderer_vulkan/vk_resource_manager.h
@@ -97,7 +97,7 @@ private:
97class VKFenceWatch final : public VKResource { 97class VKFenceWatch final : public VKResource {
98public: 98public:
99 explicit VKFenceWatch(); 99 explicit VKFenceWatch();
100 ~VKFenceWatch(); 100 ~VKFenceWatch() override;
101 101
102 /// Waits for the fence to be released. 102 /// Waits for the fence to be released.
103 void Wait(); 103 void Wait();
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
new file mode 100644
index 000000000..08279e562
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -0,0 +1,210 @@
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 <array>
7#include <limits>
8#include <vector>
9
10#include "common/assert.h"
11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/frontend/framebuffer_layout.h"
14#include "video_core/renderer_vulkan/declarations.h"
15#include "video_core/renderer_vulkan/vk_device.h"
16#include "video_core/renderer_vulkan/vk_resource_manager.h"
17#include "video_core/renderer_vulkan/vk_swapchain.h"
18
19namespace Vulkan {
20
21namespace {
22vk::SurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats) {
23 if (formats.size() == 1 && formats[0].format == vk::Format::eUndefined) {
24 return {vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear};
25 }
26 const auto& found = std::find_if(formats.begin(), formats.end(), [](const auto& format) {
27 return format.format == vk::Format::eB8G8R8A8Unorm &&
28 format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear;
29 });
30 return found != formats.end() ? *found : formats[0];
31}
32
33vk::PresentModeKHR ChooseSwapPresentMode(const std::vector<vk::PresentModeKHR>& modes) {
34 // Mailbox doesn't lock the application like fifo (vsync), prefer it
35 const auto& found = std::find_if(modes.begin(), modes.end(), [](const auto& mode) {
36 return mode == vk::PresentModeKHR::eMailbox;
37 });
38 return found != modes.end() ? *found : vk::PresentModeKHR::eFifo;
39}
40
41vk::Extent2D ChooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width,
42 u32 height) {
43 constexpr auto undefined_size{std::numeric_limits<u32>::max()};
44 if (capabilities.currentExtent.width != undefined_size) {
45 return capabilities.currentExtent;
46 }
47 vk::Extent2D extent = {width, height};
48 extent.width = std::max(capabilities.minImageExtent.width,
49 std::min(capabilities.maxImageExtent.width, extent.width));
50 extent.height = std::max(capabilities.minImageExtent.height,
51 std::min(capabilities.maxImageExtent.height, extent.height));
52 return extent;
53}
54} // namespace
55
56VKSwapchain::VKSwapchain(vk::SurfaceKHR surface, const VKDevice& device)
57 : surface{surface}, device{device} {}
58
59VKSwapchain::~VKSwapchain() = default;
60
61void VKSwapchain::Create(u32 width, u32 height) {
62 const auto dev = device.GetLogical();
63 const auto& dld = device.GetDispatchLoader();
64 const auto physical_device = device.GetPhysical();
65
66 const vk::SurfaceCapabilitiesKHR capabilities{
67 physical_device.getSurfaceCapabilitiesKHR(surface, dld)};
68 if (capabilities.maxImageExtent.width == 0 || capabilities.maxImageExtent.height == 0) {
69 return;
70 }
71
72 dev.waitIdle(dld);
73 Destroy();
74
75 CreateSwapchain(capabilities, width, height);
76 CreateSemaphores();
77 CreateImageViews();
78
79 fences.resize(image_count, nullptr);
80}
81
82void VKSwapchain::AcquireNextImage() {
83 const auto dev{device.GetLogical()};
84 const auto& dld{device.GetDispatchLoader()};
85 dev.acquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(),
86 *present_semaphores[frame_index], {}, &image_index, dld);
87
88 if (auto& fence = fences[image_index]; fence) {
89 fence->Wait();
90 fence->Release();
91 fence = nullptr;
92 }
93}
94
95bool VKSwapchain::Present(vk::Semaphore render_semaphore, VKFence& fence) {
96 const vk::Semaphore present_semaphore{*present_semaphores[frame_index]};
97 const std::array<vk::Semaphore, 2> semaphores{present_semaphore, render_semaphore};
98 const u32 wait_semaphore_count{render_semaphore ? 2U : 1U};
99 const auto& dld{device.GetDispatchLoader()};
100 const auto present_queue{device.GetPresentQueue()};
101 bool recreated = false;
102
103 const vk::PresentInfoKHR present_info(wait_semaphore_count, semaphores.data(), 1,
104 &swapchain.get(), &image_index, {});
105 switch (const auto result = present_queue.presentKHR(&present_info, dld); result) {
106 case vk::Result::eSuccess:
107 break;
108 case vk::Result::eErrorOutOfDateKHR:
109 if (current_width > 0 && current_height > 0) {
110 Create(current_width, current_height);
111 recreated = true;
112 }
113 break;
114 default:
115 LOG_CRITICAL(Render_Vulkan, "Vulkan failed to present swapchain due to {}!",
116 vk::to_string(result));
117 UNREACHABLE();
118 }
119
120 ASSERT(fences[image_index] == nullptr);
121 fences[image_index] = &fence;
122 frame_index = (frame_index + 1) % image_count;
123 return recreated;
124}
125
126bool VKSwapchain::HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const {
127 // TODO(Rodrigo): Handle framebuffer pixel format changes
128 return framebuffer.width != current_width || framebuffer.height != current_height;
129}
130
131void VKSwapchain::CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width,
132 u32 height) {
133 const auto dev{device.GetLogical()};
134 const auto& dld{device.GetDispatchLoader()};
135 const auto physical_device{device.GetPhysical()};
136
137 const std::vector<vk::SurfaceFormatKHR> formats{
138 physical_device.getSurfaceFormatsKHR(surface, dld)};
139
140 const std::vector<vk::PresentModeKHR> present_modes{
141 physical_device.getSurfacePresentModesKHR(surface, dld)};
142
143 const vk::SurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
144 const vk::PresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
145 extent = ChooseSwapExtent(capabilities, width, height);
146
147 current_width = extent.width;
148 current_height = extent.height;
149
150 u32 requested_image_count{capabilities.minImageCount + 1};
151 if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) {
152 requested_image_count = capabilities.maxImageCount;
153 }
154
155 vk::SwapchainCreateInfoKHR swapchain_ci(
156 {}, surface, requested_image_count, surface_format.format, surface_format.colorSpace,
157 extent, 1, vk::ImageUsageFlagBits::eColorAttachment, {}, {}, {},
158 capabilities.currentTransform, vk::CompositeAlphaFlagBitsKHR::eOpaque, present_mode, false,
159 {});
160
161 const u32 graphics_family{device.GetGraphicsFamily()};
162 const u32 present_family{device.GetPresentFamily()};
163 const std::array<u32, 2> queue_indices{graphics_family, present_family};
164 if (graphics_family != present_family) {
165 swapchain_ci.imageSharingMode = vk::SharingMode::eConcurrent;
166 swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size());
167 swapchain_ci.pQueueFamilyIndices = queue_indices.data();
168 } else {
169 swapchain_ci.imageSharingMode = vk::SharingMode::eExclusive;
170 }
171
172 swapchain = dev.createSwapchainKHRUnique(swapchain_ci, nullptr, dld);
173
174 images = dev.getSwapchainImagesKHR(*swapchain, dld);
175 image_count = static_cast<u32>(images.size());
176 image_format = surface_format.format;
177}
178
179void VKSwapchain::CreateSemaphores() {
180 const auto dev{device.GetLogical()};
181 const auto& dld{device.GetDispatchLoader()};
182
183 present_semaphores.resize(image_count);
184 for (std::size_t i = 0; i < image_count; i++) {
185 present_semaphores[i] = dev.createSemaphoreUnique({}, nullptr, dld);
186 }
187}
188
189void VKSwapchain::CreateImageViews() {
190 const auto dev{device.GetLogical()};
191 const auto& dld{device.GetDispatchLoader()};
192
193 image_views.resize(image_count);
194 for (std::size_t i = 0; i < image_count; i++) {
195 const vk::ImageViewCreateInfo image_view_ci({}, images[i], vk::ImageViewType::e2D,
196 image_format, {},
197 {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
198 image_views[i] = dev.createImageViewUnique(image_view_ci, nullptr, dld);
199 }
200}
201
202void VKSwapchain::Destroy() {
203 frame_index = 0;
204 present_semaphores.clear();
205 framebuffers.clear();
206 image_views.clear();
207 swapchain.reset();
208}
209
210} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
new file mode 100644
index 000000000..2ad84f185
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -0,0 +1,92 @@
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#include "video_core/renderer_vulkan/declarations.h"
11
12namespace Layout {
13struct FramebufferLayout;
14}
15
16namespace Vulkan {
17
18class VKDevice;
19class VKFence;
20
21class VKSwapchain {
22public:
23 explicit VKSwapchain(vk::SurfaceKHR surface, const VKDevice& device);
24 ~VKSwapchain();
25
26 /// Creates (or recreates) the swapchain with a given size.
27 void Create(u32 width, u32 height);
28
29 /// Acquires the next image in the swapchain, waits as needed.
30 void AcquireNextImage();
31
32 /// Presents the rendered image to the swapchain. Returns true when the swapchains had to be
33 /// recreated. Takes responsability for the ownership of fence.
34 bool Present(vk::Semaphore render_semaphore, VKFence& fence);
35
36 /// Returns true when the framebuffer layout has changed.
37 bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const;
38
39 const vk::Extent2D& GetSize() const {
40 return extent;
41 }
42
43 u32 GetImageCount() const {
44 return image_count;
45 }
46
47 u32 GetImageIndex() const {
48 return image_index;
49 }
50
51 vk::Image GetImageIndex(u32 index) const {
52 return images[index];
53 }
54
55 vk::ImageView GetImageViewIndex(u32 index) const {
56 return *image_views[index];
57 }
58
59 vk::Format GetImageFormat() const {
60 return image_format;
61 }
62
63private:
64 void CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width, u32 height);
65 void CreateSemaphores();
66 void CreateImageViews();
67
68 void Destroy();
69
70 const vk::SurfaceKHR surface;
71 const VKDevice& device;
72
73 UniqueSwapchainKHR swapchain;
74
75 u32 image_count{};
76 std::vector<vk::Image> images;
77 std::vector<UniqueImageView> image_views;
78 std::vector<UniqueFramebuffer> framebuffers;
79 std::vector<VKFence*> fences;
80 std::vector<UniqueSemaphore> present_semaphores;
81
82 u32 image_index{};
83 u32 frame_index{};
84
85 vk::Format image_format{};
86 vk::Extent2D extent{};
87
88 u32 current_width{};
89 u32 current_height{};
90};
91
92} // namespace Vulkan
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index cad7340f5..995d0e068 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -6,7 +6,6 @@
6#include <cstring> 6#include <cstring>
7#include "common/alignment.h" 7#include "common/alignment.h"
8#include "common/assert.h" 8#include "common/assert.h"
9#include "core/memory.h"
10#include "video_core/gpu.h" 9#include "video_core/gpu.h"
11#include "video_core/textures/decoders.h" 10#include "video_core/textures/decoders.h"
12#include "video_core/textures/texture.h" 11#include "video_core/textures/texture.h"
@@ -230,18 +229,18 @@ u32 BytesPerPixel(TextureFormat format) {
230 } 229 }
231} 230}
232 231
233void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y, 232void UnswizzleTexture(u8* const unswizzled_data, u8* address, u32 tile_size_x, u32 tile_size_y,
234 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, 233 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height,
235 u32 block_depth, u32 width_spacing) { 234 u32 block_depth, u32 width_spacing) {
236 CopySwizzledData((width + tile_size_x - 1) / tile_size_x, 235 CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
237 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel, 236 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
238 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true, 237 bytes_per_pixel, address, unswizzled_data, true, block_height, block_depth,
239 block_height, block_depth, width_spacing); 238 width_spacing);
240} 239}
241 240
242std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 241std::vector<u8> UnswizzleTexture(u8* address, u32 tile_size_x, u32 tile_size_y, u32 bytes_per_pixel,
243 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 242 u32 width, u32 height, u32 depth, u32 block_height,
244 u32 block_height, u32 block_depth, u32 width_spacing) { 243 u32 block_depth, u32 width_spacing) {
245 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); 244 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
246 UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel, 245 UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel,
247 width, height, depth, block_height, block_depth, width_spacing); 246 width, height, depth, block_height, block_depth, width_spacing);
@@ -249,8 +248,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y
249} 248}
250 249
251void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 250void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
252 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 251 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height) {
253 u32 block_height) {
254 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) / 252 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) /
255 gob_size_x}; 253 gob_size_x};
256 for (u32 line = 0; line < subrect_height; ++line) { 254 for (u32 line = 0; line < subrect_height; ++line) {
@@ -262,17 +260,17 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
262 const u32 gob_address = 260 const u32 gob_address =
263 gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height; 261 gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height;
264 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x]; 262 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x];
265 const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel; 263 u8* source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
266 const VAddr dest_addr = swizzled_data + swizzled_offset; 264 u8* dest_addr = swizzled_data + swizzled_offset;
267 265
268 Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel); 266 std::memcpy(dest_addr, source_line, bytes_per_pixel);
269 } 267 }
270 } 268 }
271} 269}
272 270
273void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, 271void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
274 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 272 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
275 u32 block_height, u32 offset_x, u32 offset_y) { 273 u32 offset_x, u32 offset_y) {
276 for (u32 line = 0; line < subrect_height; ++line) { 274 for (u32 line = 0; line < subrect_height; ++line) {
277 const u32 y2 = line + offset_y; 275 const u32 y2 = line + offset_y;
278 const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height + 276 const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height +
@@ -282,10 +280,10 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
282 const u32 x2 = (x + offset_x) * bytes_per_pixel; 280 const u32 x2 = (x + offset_x) * bytes_per_pixel;
283 const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height; 281 const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height;
284 const u32 swizzled_offset = gob_address + table[x2 % gob_size_x]; 282 const u32 swizzled_offset = gob_address + table[x2 % gob_size_x];
285 const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel; 283 u8* dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
286 const VAddr source_addr = swizzled_data + swizzled_offset; 284 u8* source_addr = swizzled_data + swizzled_offset;
287 285
288 Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel); 286 std::memcpy(dest_line, source_addr, bytes_per_pixel);
289 } 287 }
290 } 288 }
291} 289}
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 65df86890..e078fa274 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -17,14 +17,14 @@ inline std::size_t GetGOBSize() {
17} 17}
18 18
19/// Unswizzles a swizzled texture without changing its format. 19/// Unswizzles a swizzled texture without changing its format.
20void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y, 20void UnswizzleTexture(u8* unswizzled_data, u8* address, u32 tile_size_x, u32 tile_size_y,
21 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 21 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
22 u32 block_height = TICEntry::DefaultBlockHeight, 22 u32 block_height = TICEntry::DefaultBlockHeight,
23 u32 block_depth = TICEntry::DefaultBlockHeight, u32 width_spacing = 0); 23 u32 block_depth = TICEntry::DefaultBlockHeight, u32 width_spacing = 0);
24 24
25/// Unswizzles a swizzled texture without changing its format. 25/// Unswizzles a swizzled texture without changing its format.
26std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 26std::vector<u8> UnswizzleTexture(u8* address, u32 tile_size_x, u32 tile_size_y, u32 bytes_per_pixel,
27 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 27 u32 width, u32 height, u32 depth,
28 u32 block_height = TICEntry::DefaultBlockHeight, 28 u32 block_height = TICEntry::DefaultBlockHeight,
29 u32 block_depth = TICEntry::DefaultBlockHeight, 29 u32 block_depth = TICEntry::DefaultBlockHeight,
30 u32 width_spacing = 0); 30 u32 width_spacing = 0);
@@ -44,12 +44,11 @@ std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height
44 44
45/// Copies an untiled subrectangle into a tiled surface. 45/// Copies an untiled subrectangle into a tiled surface.
46void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 46void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
47 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 47 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height);
48 u32 block_height);
49 48
50/// Copies a tiled subrectangle into a linear surface. 49/// Copies a tiled subrectangle into a linear surface.
51void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, 50void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
52 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 51 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
53 u32 block_height, u32 offset_x, u32 offset_y); 52 u32 offset_x, u32 offset_y);
54 53
55} // namespace Tegra::Texture 54} // namespace Tegra::Texture
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 40da1a4e2..dc149d2ed 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -24,7 +24,7 @@ constexpr u32 TIMEOUT_SECONDS = 30;
24struct Client::Impl { 24struct Client::Impl {
25 Impl(std::string host, std::string username, std::string token) 25 Impl(std::string host, std::string username, std::string token)
26 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} { 26 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
27 std::lock_guard<std::mutex> lock(jwt_cache.mutex); 27 std::lock_guard lock{jwt_cache.mutex};
28 if (this->username == jwt_cache.username && this->token == jwt_cache.token) { 28 if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
29 jwt = jwt_cache.jwt; 29 jwt = jwt_cache.jwt;
30 } 30 }
@@ -151,7 +151,7 @@ struct Client::Impl {
151 if (result.result_code != Common::WebResult::Code::Success) { 151 if (result.result_code != Common::WebResult::Code::Success) {
152 LOG_ERROR(WebService, "UpdateJWT failed"); 152 LOG_ERROR(WebService, "UpdateJWT failed");
153 } else { 153 } else {
154 std::lock_guard<std::mutex> lock(jwt_cache.mutex); 154 std::lock_guard lock{jwt_cache.mutex};
155 jwt_cache.username = username; 155 jwt_cache.username = username;
156 jwt_cache.token = token; 156 jwt_cache.token = token;
157 jwt_cache.jwt = jwt = result.returned_data; 157 jwt_cache.jwt = jwt = result.returned_data;
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
index 5c1b65a2c..f95f7fe3c 100644
--- a/src/yuzu/applets/profile_select.cpp
+++ b/src/yuzu/applets/profile_select.cpp
@@ -58,10 +58,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
58 58
59 scroll_area = new QScrollArea; 59 scroll_area = new QScrollArea;
60 60
61 buttons = new QDialogButtonBox; 61 buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
62 buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
63 buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole);
64
65 connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept); 62 connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept);
66 connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject); 63 connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject);
67 64
@@ -163,6 +160,6 @@ void QtProfileSelector::SelectProfile(
163 160
164void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) { 161void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) {
165 // Acquire the HLE mutex 162 // Acquire the HLE mutex
166 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 163 std::lock_guard lock{HLE::g_hle_lock};
167 callback(uuid); 164 callback(uuid);
168} 165}
diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp
index 8a26fdff1..f3eb29b25 100644
--- a/src/yuzu/applets/software_keyboard.cpp
+++ b/src/yuzu/applets/software_keyboard.cpp
@@ -75,13 +75,13 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
75 length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length)); 75 length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length));
76 }); 76 });
77 77
78 buttons = new QDialogButtonBox; 78 buttons = new QDialogButtonBox(QDialogButtonBox::Cancel);
79 buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); 79 if (parameters.submit_text.empty()) {
80 buttons->addButton(parameters.submit_text.empty() 80 buttons->addButton(QDialogButtonBox::Ok);
81 ? tr("OK") 81 } else {
82 : QString::fromStdU16String(parameters.submit_text), 82 buttons->addButton(QString::fromStdU16String(parameters.submit_text),
83 QDialogButtonBox::AcceptRole); 83 QDialogButtonBox::AcceptRole);
84 84 }
85 connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept); 85 connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept);
86 connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject); 86 connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject);
87 layout->addWidget(header_label); 87 layout->addWidget(header_label);
@@ -141,12 +141,12 @@ void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message,
141 141
142void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { 142void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) {
143 // Acquire the HLE mutex 143 // Acquire the HLE mutex
144 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 144 std::lock_guard lock{HLE::g_hle_lock};
145 text_output(text); 145 text_output(text);
146} 146}
147 147
148void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { 148void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() {
149 // Acquire the HLE mutex 149 // Acquire the HLE mutex
150 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 150 std::lock_guard lock{HLE::g_hle_lock};
151 finished_check(); 151 finished_check();
152} 152}
diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp
index 979b9ec14..ac80b2fa2 100644
--- a/src/yuzu/applets/web_browser.cpp
+++ b/src/yuzu/applets/web_browser.cpp
@@ -104,12 +104,12 @@ void QtWebBrowser::OpenPage(std::string_view url, std::function<void()> unpack_r
104 104
105void QtWebBrowser::MainWindowUnpackRomFS() { 105void QtWebBrowser::MainWindowUnpackRomFS() {
106 // Acquire the HLE mutex 106 // Acquire the HLE mutex
107 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 107 std::lock_guard lock{HLE::g_hle_lock};
108 unpack_romfs_callback(); 108 unpack_romfs_callback();
109} 109}
110 110
111void QtWebBrowser::MainWindowFinishedBrowsing() { 111void QtWebBrowser::MainWindowFinishedBrowsing() {
112 // Acquire the HLE mutex 112 // Acquire the HLE mutex
113 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 113 std::lock_guard lock{HLE::g_hle_lock};
114 finished_callback(); 114 finished_callback();
115} 115}
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index d2c97b1f8..7438fbc0a 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -24,8 +24,6 @@ void EmuThread::run() {
24 24
25 MicroProfileOnThreadCreate("EmuThread"); 25 MicroProfileOnThreadCreate("EmuThread");
26 26
27 stop_run = false;
28
29 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 27 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
30 28
31 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( 29 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources(
@@ -40,7 +38,7 @@ void EmuThread::run() {
40 render_window->DoneCurrent(); 38 render_window->DoneCurrent();
41 } 39 }
42 40
43 // holds whether the cpu was running during the last iteration, 41 // Holds whether the cpu was running during the last iteration,
44 // so that the DebugModeLeft signal can be emitted before the 42 // so that the DebugModeLeft signal can be emitted before the
45 // next execution step 43 // next execution step
46 bool was_active = false; 44 bool was_active = false;
@@ -69,7 +67,7 @@ void EmuThread::run() {
69 67
70 was_active = false; 68 was_active = false;
71 } else { 69 } else {
72 std::unique_lock<std::mutex> lock(running_mutex); 70 std::unique_lock lock{running_mutex};
73 running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); 71 running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; });
74 } 72 }
75 } 73 }
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 7226e690e..3183621bc 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -53,7 +53,7 @@ public:
53 * @note This function is thread-safe 53 * @note This function is thread-safe
54 */ 54 */
55 void SetRunning(bool running) { 55 void SetRunning(bool running) {
56 std::unique_lock<std::mutex> lock(running_mutex); 56 std::unique_lock lock{running_mutex};
57 this->running = running; 57 this->running = running;
58 lock.unlock(); 58 lock.unlock();
59 running_cv.notify_all(); 59 running_cv.notify_all();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 74dc6bb28..dead9f807 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -209,7 +209,7 @@ void Config::ReadPlayerValues() {
209 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 209 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
210 auto& player = Settings::values.players[p]; 210 auto& player = Settings::values.players[p];
211 211
212 player.connected = qt_config->value(QString("player_%1_connected").arg(p), false).toBool(); 212 player.connected = ReadSetting(QString("player_%1_connected").arg(p), false).toBool();
213 213
214 player.type = static_cast<Settings::ControllerType>( 214 player.type = static_cast<Settings::ControllerType>(
215 qt_config 215 qt_config
@@ -269,7 +269,7 @@ void Config::ReadPlayerValues() {
269} 269}
270 270
271void Config::ReadDebugValues() { 271void Config::ReadDebugValues() {
272 Settings::values.debug_pad_enabled = qt_config->value("debug_pad_enabled", false).toBool(); 272 Settings::values.debug_pad_enabled = ReadSetting("debug_pad_enabled", false).toBool();
273 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 273 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
274 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 274 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
275 Settings::values.debug_pad_buttons[i] = 275 Settings::values.debug_pad_buttons[i] =
@@ -298,7 +298,7 @@ void Config::ReadDebugValues() {
298} 298}
299 299
300void Config::ReadKeyboardValues() { 300void Config::ReadKeyboardValues() {
301 Settings::values.keyboard_enabled = qt_config->value("keyboard_enabled", false).toBool(); 301 Settings::values.keyboard_enabled = ReadSetting("keyboard_enabled", false).toBool();
302 302
303 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(), 303 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
304 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); 304 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
@@ -311,7 +311,7 @@ void Config::ReadKeyboardValues() {
311} 311}
312 312
313void Config::ReadMouseValues() { 313void Config::ReadMouseValues() {
314 Settings::values.mouse_enabled = qt_config->value("mouse_enabled", false).toBool(); 314 Settings::values.mouse_enabled = ReadSetting("mouse_enabled", false).toBool();
315 315
316 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { 316 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
317 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); 317 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
@@ -327,16 +327,14 @@ void Config::ReadMouseValues() {
327} 327}
328 328
329void Config::ReadTouchscreenValues() { 329void Config::ReadTouchscreenValues() {
330 Settings::values.touchscreen.enabled = qt_config->value("touchscreen_enabled", true).toBool(); 330 Settings::values.touchscreen.enabled = ReadSetting("touchscreen_enabled", true).toBool();
331 Settings::values.touchscreen.device = 331 Settings::values.touchscreen.device =
332 qt_config->value("touchscreen_device", "engine:emu_window").toString().toStdString(); 332 ReadSetting("touchscreen_device", "engine:emu_window").toString().toStdString();
333 333
334 Settings::values.touchscreen.finger = qt_config->value("touchscreen_finger", 0).toUInt(); 334 Settings::values.touchscreen.finger = ReadSetting("touchscreen_finger", 0).toUInt();
335 Settings::values.touchscreen.rotation_angle = qt_config->value("touchscreen_angle", 0).toUInt(); 335 Settings::values.touchscreen.rotation_angle = ReadSetting("touchscreen_angle", 0).toUInt();
336 Settings::values.touchscreen.diameter_x = 336 Settings::values.touchscreen.diameter_x = ReadSetting("touchscreen_diameter_x", 15).toUInt();
337 qt_config->value("touchscreen_diameter_x", 15).toUInt(); 337 Settings::values.touchscreen.diameter_y = ReadSetting("touchscreen_diameter_y", 15).toUInt();
338 Settings::values.touchscreen.diameter_y =
339 qt_config->value("touchscreen_diameter_y", 15).toUInt();
340 qt_config->endGroup(); 338 qt_config->endGroup();
341} 339}
342 340
@@ -357,42 +355,41 @@ void Config::ReadValues() {
357 ReadTouchscreenValues(); 355 ReadTouchscreenValues();
358 356
359 Settings::values.motion_device = 357 Settings::values.motion_device =
360 qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") 358 ReadSetting("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
361 .toString() 359 .toString()
362 .toStdString(); 360 .toStdString();
363 361
364 qt_config->beginGroup("Core"); 362 qt_config->beginGroup("Core");
365 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); 363 Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool();
366 Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool(); 364 Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool();
367 qt_config->endGroup(); 365 qt_config->endGroup();
368 366
369 qt_config->beginGroup("Renderer"); 367 qt_config->beginGroup("Renderer");
370 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); 368 Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat();
371 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool(); 369 Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool();
372 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); 370 Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt();
373 Settings::values.use_disk_shader_cache = 371 Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool();
374 qt_config->value("use_disk_shader_cache", false).toBool();
375 Settings::values.use_accurate_gpu_emulation = 372 Settings::values.use_accurate_gpu_emulation =
376 qt_config->value("use_accurate_gpu_emulation", false).toBool(); 373 ReadSetting("use_accurate_gpu_emulation", false).toBool();
377 Settings::values.use_asynchronous_gpu_emulation = 374 Settings::values.use_asynchronous_gpu_emulation =
378 qt_config->value("use_asynchronous_gpu_emulation", false).toBool(); 375 ReadSetting("use_asynchronous_gpu_emulation", false).toBool();
379 376
380 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); 377 Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat();
381 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); 378 Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat();
382 Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat(); 379 Settings::values.bg_blue = ReadSetting("bg_blue", 0.0).toFloat();
383 qt_config->endGroup(); 380 qt_config->endGroup();
384 381
385 qt_config->beginGroup("Audio"); 382 qt_config->beginGroup("Audio");
386 Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); 383 Settings::values.sink_id = ReadSetting("output_engine", "auto").toString().toStdString();
387 Settings::values.enable_audio_stretching = 384 Settings::values.enable_audio_stretching =
388 qt_config->value("enable_audio_stretching", true).toBool(); 385 ReadSetting("enable_audio_stretching", true).toBool();
389 Settings::values.audio_device_id = 386 Settings::values.audio_device_id =
390 qt_config->value("output_device", "auto").toString().toStdString(); 387 ReadSetting("output_device", "auto").toString().toStdString();
391 Settings::values.volume = qt_config->value("volume", 1).toFloat(); 388 Settings::values.volume = ReadSetting("volume", 1).toFloat();
392 qt_config->endGroup(); 389 qt_config->endGroup();
393 390
394 qt_config->beginGroup("Data Storage"); 391 qt_config->beginGroup("Data Storage");
395 Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); 392 Settings::values.use_virtual_sd = ReadSetting("use_virtual_sd", true).toBool();
396 FileUtil::GetUserPath( 393 FileUtil::GetUserPath(
397 FileUtil::UserPath::NANDDir, 394 FileUtil::UserPath::NANDDir,
398 qt_config 395 qt_config
@@ -410,30 +407,29 @@ void Config::ReadValues() {
410 qt_config->endGroup(); 407 qt_config->endGroup();
411 408
412 qt_config->beginGroup("Core"); 409 qt_config->beginGroup("Core");
413 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); 410 Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool();
414 Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool(); 411 Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool();
415 qt_config->endGroup(); 412 qt_config->endGroup();
416 413
417 qt_config->beginGroup("System"); 414 qt_config->beginGroup("System");
418 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 415 Settings::values.use_docked_mode = ReadSetting("use_docked_mode", false).toBool();
419 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
420 416
421 Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0, 417 Settings::values.current_user =
422 Service::Account::MAX_USERS - 1); 418 std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1);
423 419
424 Settings::values.language_index = qt_config->value("language_index", 1).toInt(); 420 Settings::values.language_index = ReadSetting("language_index", 1).toInt();
425 421
426 const auto rng_seed_enabled = qt_config->value("rng_seed_enabled", false).toBool(); 422 const auto rng_seed_enabled = ReadSetting("rng_seed_enabled", false).toBool();
427 if (rng_seed_enabled) { 423 if (rng_seed_enabled) {
428 Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong(); 424 Settings::values.rng_seed = ReadSetting("rng_seed", 0).toULongLong();
429 } else { 425 } else {
430 Settings::values.rng_seed = std::nullopt; 426 Settings::values.rng_seed = std::nullopt;
431 } 427 }
432 428
433 const auto custom_rtc_enabled = qt_config->value("custom_rtc_enabled", false).toBool(); 429 const auto custom_rtc_enabled = ReadSetting("custom_rtc_enabled", false).toBool();
434 if (custom_rtc_enabled) { 430 if (custom_rtc_enabled) {
435 Settings::values.custom_rtc = 431 Settings::values.custom_rtc =
436 std::chrono::seconds(qt_config->value("custom_rtc", 0).toULongLong()); 432 std::chrono::seconds(ReadSetting("custom_rtc", 0).toULongLong());
437 } else { 433 } else {
438 Settings::values.custom_rtc = std::nullopt; 434 Settings::values.custom_rtc = std::nullopt;
439 } 435 }
@@ -441,35 +437,35 @@ void Config::ReadValues() {
441 qt_config->endGroup(); 437 qt_config->endGroup();
442 438
443 qt_config->beginGroup("Miscellaneous"); 439 qt_config->beginGroup("Miscellaneous");
444 Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); 440 Settings::values.log_filter = ReadSetting("log_filter", "*:Info").toString().toStdString();
445 Settings::values.use_dev_keys = qt_config->value("use_dev_keys", false).toBool(); 441 Settings::values.use_dev_keys = ReadSetting("use_dev_keys", false).toBool();
446 qt_config->endGroup(); 442 qt_config->endGroup();
447 443
448 qt_config->beginGroup("Debugging"); 444 qt_config->beginGroup("Debugging");
449 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 445 Settings::values.use_gdbstub = ReadSetting("use_gdbstub", false).toBool();
450 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 446 Settings::values.gdbstub_port = ReadSetting("gdbstub_port", 24689).toInt();
451 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); 447 Settings::values.program_args = ReadSetting("program_args", "").toString().toStdString();
452 Settings::values.dump_exefs = qt_config->value("dump_exefs", false).toBool(); 448 Settings::values.dump_exefs = ReadSetting("dump_exefs", false).toBool();
453 Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); 449 Settings::values.dump_nso = ReadSetting("dump_nso", false).toBool();
454 qt_config->endGroup(); 450 qt_config->endGroup();
455 451
456 qt_config->beginGroup("WebService"); 452 qt_config->beginGroup("WebService");
457 Settings::values.enable_telemetry = qt_config->value("enable_telemetry", true).toBool(); 453 Settings::values.enable_telemetry = ReadSetting("enable_telemetry", true).toBool();
458 Settings::values.web_api_url = 454 Settings::values.web_api_url =
459 qt_config->value("web_api_url", "https://api.yuzu-emu.org").toString().toStdString(); 455 ReadSetting("web_api_url", "https://api.yuzu-emu.org").toString().toStdString();
460 Settings::values.yuzu_username = qt_config->value("yuzu_username").toString().toStdString(); 456 Settings::values.yuzu_username = ReadSetting("yuzu_username").toString().toStdString();
461 Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString(); 457 Settings::values.yuzu_token = ReadSetting("yuzu_token").toString().toStdString();
462 qt_config->endGroup(); 458 qt_config->endGroup();
463 459
464 const auto size = qt_config->beginReadArray("DisabledAddOns"); 460 const auto size = qt_config->beginReadArray("DisabledAddOns");
465 for (int i = 0; i < size; ++i) { 461 for (int i = 0; i < size; ++i) {
466 qt_config->setArrayIndex(i); 462 qt_config->setArrayIndex(i);
467 const auto title_id = qt_config->value("title_id", 0).toULongLong(); 463 const auto title_id = ReadSetting("title_id", 0).toULongLong();
468 std::vector<std::string> out; 464 std::vector<std::string> out;
469 const auto d_size = qt_config->beginReadArray("disabled"); 465 const auto d_size = qt_config->beginReadArray("disabled");
470 for (int j = 0; j < d_size; ++j) { 466 for (int j = 0; j < d_size; ++j) {
471 qt_config->setArrayIndex(j); 467 qt_config->setArrayIndex(j);
472 out.push_back(qt_config->value("d", "").toString().toStdString()); 468 out.push_back(ReadSetting("d", "").toString().toStdString());
473 } 469 }
474 qt_config->endArray(); 470 qt_config->endArray();
475 Settings::values.disabled_addons.insert_or_assign(title_id, out); 471 Settings::values.disabled_addons.insert_or_assign(title_id, out);
@@ -477,41 +473,38 @@ void Config::ReadValues() {
477 qt_config->endArray(); 473 qt_config->endArray();
478 474
479 qt_config->beginGroup("UI"); 475 qt_config->beginGroup("UI");
480 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); 476 UISettings::values.theme = ReadSetting("theme", UISettings::themes[0].second).toString();
481 UISettings::values.enable_discord_presence = 477 UISettings::values.enable_discord_presence =
482 qt_config->value("enable_discord_presence", true).toBool(); 478 ReadSetting("enable_discord_presence", true).toBool();
483 UISettings::values.screenshot_resolution_factor = 479 UISettings::values.screenshot_resolution_factor =
484 static_cast<u16>(qt_config->value("screenshot_resolution_factor", 0).toUInt()); 480 static_cast<u16>(ReadSetting("screenshot_resolution_factor", 0).toUInt());
485 UISettings::values.select_user_on_boot = 481 UISettings::values.select_user_on_boot = ReadSetting("select_user_on_boot", false).toBool();
486 qt_config->value("select_user_on_boot", false).toBool();
487 482
488 qt_config->beginGroup("UIGameList"); 483 qt_config->beginGroup("UIGameList");
489 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); 484 UISettings::values.show_unknown = ReadSetting("show_unknown", true).toBool();
490 UISettings::values.show_add_ons = qt_config->value("show_add_ons", true).toBool(); 485 UISettings::values.show_add_ons = ReadSetting("show_add_ons", true).toBool();
491 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt(); 486 UISettings::values.icon_size = ReadSetting("icon_size", 64).toUInt();
492 UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt(); 487 UISettings::values.row_1_text_id = ReadSetting("row_1_text_id", 3).toUInt();
493 UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt(); 488 UISettings::values.row_2_text_id = ReadSetting("row_2_text_id", 2).toUInt();
494 qt_config->endGroup(); 489 qt_config->endGroup();
495 490
496 qt_config->beginGroup("UILayout"); 491 qt_config->beginGroup("UILayout");
497 UISettings::values.geometry = qt_config->value("geometry").toByteArray(); 492 UISettings::values.geometry = ReadSetting("geometry").toByteArray();
498 UISettings::values.state = qt_config->value("state").toByteArray(); 493 UISettings::values.state = ReadSetting("state").toByteArray();
499 UISettings::values.renderwindow_geometry = 494 UISettings::values.renderwindow_geometry = ReadSetting("geometryRenderWindow").toByteArray();
500 qt_config->value("geometryRenderWindow").toByteArray(); 495 UISettings::values.gamelist_header_state = ReadSetting("gameListHeaderState").toByteArray();
501 UISettings::values.gamelist_header_state =
502 qt_config->value("gameListHeaderState").toByteArray();
503 UISettings::values.microprofile_geometry = 496 UISettings::values.microprofile_geometry =
504 qt_config->value("microProfileDialogGeometry").toByteArray(); 497 ReadSetting("microProfileDialogGeometry").toByteArray();
505 UISettings::values.microprofile_visible = 498 UISettings::values.microprofile_visible =
506 qt_config->value("microProfileDialogVisible", false).toBool(); 499 ReadSetting("microProfileDialogVisible", false).toBool();
507 qt_config->endGroup(); 500 qt_config->endGroup();
508 501
509 qt_config->beginGroup("Paths"); 502 qt_config->beginGroup("Paths");
510 UISettings::values.roms_path = qt_config->value("romsPath").toString(); 503 UISettings::values.roms_path = ReadSetting("romsPath").toString();
511 UISettings::values.symbols_path = qt_config->value("symbolsPath").toString(); 504 UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
512 UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString(); 505 UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString();
513 UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool(); 506 UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool();
514 UISettings::values.recent_files = qt_config->value("recentFiles").toStringList(); 507 UISettings::values.recent_files = ReadSetting("recentFiles").toStringList();
515 qt_config->endGroup(); 508 qt_config->endGroup();
516 509
517 qt_config->beginGroup("Shortcuts"); 510 qt_config->beginGroup("Shortcuts");
@@ -524,8 +517,8 @@ void Config::ReadValues() {
524 qt_config->beginGroup(hotkey); 517 qt_config->beginGroup(hotkey);
525 UISettings::values.shortcuts.emplace_back(UISettings::Shortcut( 518 UISettings::values.shortcuts.emplace_back(UISettings::Shortcut(
526 group + "/" + hotkey, 519 group + "/" + hotkey,
527 UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), 520 UISettings::ContextualShortcut(ReadSetting("KeySeq").toString(),
528 qt_config->value("Context").toInt()))); 521 ReadSetting("Context").toInt())));
529 qt_config->endGroup(); 522 qt_config->endGroup();
530 } 523 }
531 524
@@ -533,16 +526,16 @@ void Config::ReadValues() {
533 } 526 }
534 qt_config->endGroup(); 527 qt_config->endGroup();
535 528
536 UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); 529 UISettings::values.single_window_mode = ReadSetting("singleWindowMode", true).toBool();
537 UISettings::values.fullscreen = qt_config->value("fullscreen", false).toBool(); 530 UISettings::values.fullscreen = ReadSetting("fullscreen", false).toBool();
538 UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); 531 UISettings::values.display_titlebar = ReadSetting("displayTitleBars", true).toBool();
539 UISettings::values.show_filter_bar = qt_config->value("showFilterBar", true).toBool(); 532 UISettings::values.show_filter_bar = ReadSetting("showFilterBar", true).toBool();
540 UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool(); 533 UISettings::values.show_status_bar = ReadSetting("showStatusBar", true).toBool();
541 UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool(); 534 UISettings::values.confirm_before_closing = ReadSetting("confirmClose", true).toBool();
542 UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); 535 UISettings::values.first_start = ReadSetting("firstStart", true).toBool();
543 UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt(); 536 UISettings::values.callout_flags = ReadSetting("calloutFlags", 0).toUInt();
544 UISettings::values.show_console = qt_config->value("showConsole", false).toBool(); 537 UISettings::values.show_console = ReadSetting("showConsole", false).toBool();
545 UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt(); 538 UISettings::values.profile_index = ReadSetting("profileIndex", 0).toUInt();
546 539
547 ApplyDefaultProfileIfInputInvalid(); 540 ApplyDefaultProfileIfInputInvalid();
548 541
@@ -553,62 +546,79 @@ void Config::SavePlayerValues() {
553 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 546 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
554 const auto& player = Settings::values.players[p]; 547 const auto& player = Settings::values.players[p];
555 548
556 qt_config->setValue(QString("player_%1_connected").arg(p), player.connected); 549 WriteSetting(QString("player_%1_connected").arg(p), player.connected, false);
557 qt_config->setValue(QString("player_%1_type").arg(p), static_cast<u8>(player.type)); 550 WriteSetting(QString("player_%1_type").arg(p), static_cast<u8>(player.type),
551 static_cast<u8>(Settings::ControllerType::DualJoycon));
558 552
559 qt_config->setValue(QString("player_%1_body_color_left").arg(p), player.body_color_left); 553 WriteSetting(QString("player_%1_body_color_left").arg(p), player.body_color_left,
560 qt_config->setValue(QString("player_%1_body_color_right").arg(p), player.body_color_right); 554 Settings::JOYCON_BODY_NEON_BLUE);
561 qt_config->setValue(QString("player_%1_button_color_left").arg(p), 555 WriteSetting(QString("player_%1_body_color_right").arg(p), player.body_color_right,
562 player.button_color_left); 556 Settings::JOYCON_BODY_NEON_RED);
563 qt_config->setValue(QString("player_%1_button_color_right").arg(p), 557 WriteSetting(QString("player_%1_button_color_left").arg(p), player.button_color_left,
564 player.button_color_right); 558 Settings::JOYCON_BUTTONS_NEON_BLUE);
559 WriteSetting(QString("player_%1_button_color_right").arg(p), player.button_color_right,
560 Settings::JOYCON_BUTTONS_NEON_RED);
565 561
566 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 562 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
567 qt_config->setValue(QString("player_%1_").arg(p) + 563 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
568 QString::fromStdString(Settings::NativeButton::mapping[i]), 564 WriteSetting(QString("player_%1_").arg(p) +
569 QString::fromStdString(player.buttons[i])); 565 QString::fromStdString(Settings::NativeButton::mapping[i]),
566 QString::fromStdString(player.buttons[i]),
567 QString::fromStdString(default_param));
570 } 568 }
571 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 569 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
572 qt_config->setValue(QString("player_%1_").arg(p) + 570 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
573 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 571 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
574 QString::fromStdString(player.analogs[i])); 572 default_analogs[i][3], default_analogs[i][4], 0.5f);
573 WriteSetting(QString("player_%1_").arg(p) +
574 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
575 QString::fromStdString(player.analogs[i]),
576 QString::fromStdString(default_param));
575 } 577 }
576 } 578 }
577} 579}
578 580
579void Config::SaveDebugValues() { 581void Config::SaveDebugValues() {
580 qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled); 582 WriteSetting("debug_pad_enabled", Settings::values.debug_pad_enabled, false);
581 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 583 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
582 qt_config->setValue(QString("debug_pad_") + 584 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
583 QString::fromStdString(Settings::NativeButton::mapping[i]), 585 WriteSetting(QString("debug_pad_") +
584 QString::fromStdString(Settings::values.debug_pad_buttons[i])); 586 QString::fromStdString(Settings::NativeButton::mapping[i]),
587 QString::fromStdString(Settings::values.debug_pad_buttons[i]),
588 QString::fromStdString(default_param));
585 } 589 }
586 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 590 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
587 qt_config->setValue(QString("debug_pad_") + 591 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
588 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 592 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
589 QString::fromStdString(Settings::values.debug_pad_analogs[i])); 593 default_analogs[i][3], default_analogs[i][4], 0.5f);
594 WriteSetting(QString("debug_pad_") +
595 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
596 QString::fromStdString(Settings::values.debug_pad_analogs[i]),
597 QString::fromStdString(default_param));
590 } 598 }
591} 599}
592 600
593void Config::SaveMouseValues() { 601void Config::SaveMouseValues() {
594 qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled); 602 WriteSetting("mouse_enabled", Settings::values.mouse_enabled, false);
595 603
596 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { 604 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
597 qt_config->setValue(QString("mouse_") + 605 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
598 QString::fromStdString(Settings::NativeMouseButton::mapping[i]), 606 WriteSetting(QString("mouse_") +
599 QString::fromStdString(Settings::values.mouse_buttons[i])); 607 QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
608 QString::fromStdString(Settings::values.mouse_buttons[i]),
609 QString::fromStdString(default_param));
600 } 610 }
601} 611}
602 612
603void Config::SaveTouchscreenValues() { 613void Config::SaveTouchscreenValues() {
604 qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled); 614 WriteSetting("touchscreen_enabled", Settings::values.touchscreen.enabled, true);
605 qt_config->setValue("touchscreen_device", 615 WriteSetting("touchscreen_device", QString::fromStdString(Settings::values.touchscreen.device),
606 QString::fromStdString(Settings::values.touchscreen.device)); 616 "engine:emu_window");
607 617
608 qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger); 618 WriteSetting("touchscreen_finger", Settings::values.touchscreen.finger, 0);
609 qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle); 619 WriteSetting("touchscreen_angle", Settings::values.touchscreen.rotation_angle, 0);
610 qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x); 620 WriteSetting("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x, 15);
611 qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y); 621 WriteSetting("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y, 15);
612} 622}
613 623
614void Config::SaveValues() { 624void Config::SaveValues() {
@@ -619,91 +629,95 @@ void Config::SaveValues() {
619 SaveMouseValues(); 629 SaveMouseValues();
620 SaveTouchscreenValues(); 630 SaveTouchscreenValues();
621 631
622 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); 632 WriteSetting("motion_device", QString::fromStdString(Settings::values.motion_device),
623 qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled); 633 "engine:motion_emu,update_period:100,sensitivity:0.01");
634 WriteSetting("keyboard_enabled", Settings::values.keyboard_enabled, false);
624 635
625 qt_config->endGroup(); 636 qt_config->endGroup();
626 637
627 qt_config->beginGroup("Core"); 638 qt_config->beginGroup("Core");
628 qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit); 639 WriteSetting("use_cpu_jit", Settings::values.use_cpu_jit, true);
629 qt_config->setValue("use_multi_core", Settings::values.use_multi_core); 640 WriteSetting("use_multi_core", Settings::values.use_multi_core, false);
630 qt_config->endGroup(); 641 qt_config->endGroup();
631 642
632 qt_config->beginGroup("Renderer"); 643 qt_config->beginGroup("Renderer");
633 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); 644 WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0);
634 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); 645 WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true);
635 qt_config->setValue("frame_limit", Settings::values.frame_limit); 646 WriteSetting("frame_limit", Settings::values.frame_limit, 100);
636 qt_config->setValue("use_disk_shader_cache", Settings::values.use_disk_shader_cache); 647 WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true);
637 qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation); 648 WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false);
638 qt_config->setValue("use_asynchronous_gpu_emulation", 649 WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation,
639 Settings::values.use_asynchronous_gpu_emulation); 650 false);
640 651
641 // Cast to double because Qt's written float values are not human-readable 652 // Cast to double because Qt's written float values are not human-readable
642 qt_config->setValue("bg_red", (double)Settings::values.bg_red); 653 WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0);
643 qt_config->setValue("bg_green", (double)Settings::values.bg_green); 654 WriteSetting("bg_green", (double)Settings::values.bg_green, 0.0);
644 qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); 655 WriteSetting("bg_blue", (double)Settings::values.bg_blue, 0.0);
645 qt_config->endGroup(); 656 qt_config->endGroup();
646 657
647 qt_config->beginGroup("Audio"); 658 qt_config->beginGroup("Audio");
648 qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); 659 WriteSetting("output_engine", QString::fromStdString(Settings::values.sink_id), "auto");
649 qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching); 660 WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true);
650 qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id)); 661 WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto");
651 qt_config->setValue("volume", Settings::values.volume); 662 WriteSetting("volume", Settings::values.volume, 1.0f);
652 qt_config->endGroup(); 663 qt_config->endGroup();
653 664
654 qt_config->beginGroup("Data Storage"); 665 qt_config->beginGroup("Data Storage");
655 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); 666 WriteSetting("use_virtual_sd", Settings::values.use_virtual_sd, true);
656 qt_config->setValue("nand_directory", 667 WriteSetting("nand_directory",
657 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 668 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
658 qt_config->setValue("sdmc_directory", 669 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
659 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 670 WriteSetting("sdmc_directory",
671 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
672 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
660 qt_config->endGroup(); 673 qt_config->endGroup();
661 674
662 qt_config->beginGroup("System"); 675 qt_config->beginGroup("System");
663 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 676 WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false);
664 qt_config->setValue("enable_nfc", Settings::values.enable_nfc); 677 WriteSetting("current_user", Settings::values.current_user, 0);
665 qt_config->setValue("current_user", Settings::values.current_user); 678 WriteSetting("language_index", Settings::values.language_index, 1);
666 qt_config->setValue("language_index", Settings::values.language_index);
667 679
668 qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value()); 680 WriteSetting("rng_seed_enabled", Settings::values.rng_seed.has_value(), false);
669 qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0)); 681 WriteSetting("rng_seed", Settings::values.rng_seed.value_or(0), 0);
670 682
671 qt_config->setValue("custom_rtc_enabled", Settings::values.custom_rtc.has_value()); 683 WriteSetting("custom_rtc_enabled", Settings::values.custom_rtc.has_value(), false);
672 qt_config->setValue("custom_rtc", 684 WriteSetting("custom_rtc",
673 QVariant::fromValue<long long>( 685 QVariant::fromValue<long long>(
674 Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count())); 686 Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
687 0);
675 688
676 qt_config->endGroup(); 689 qt_config->endGroup();
677 690
678 qt_config->beginGroup("Miscellaneous"); 691 qt_config->beginGroup("Miscellaneous");
679 qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); 692 WriteSetting("log_filter", QString::fromStdString(Settings::values.log_filter), "*:Info");
680 qt_config->setValue("use_dev_keys", Settings::values.use_dev_keys); 693 WriteSetting("use_dev_keys", Settings::values.use_dev_keys, false);
681 qt_config->endGroup(); 694 qt_config->endGroup();
682 695
683 qt_config->beginGroup("Debugging"); 696 qt_config->beginGroup("Debugging");
684 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 697 WriteSetting("use_gdbstub", Settings::values.use_gdbstub, false);
685 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 698 WriteSetting("gdbstub_port", Settings::values.gdbstub_port, 24689);
686 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); 699 WriteSetting("program_args", QString::fromStdString(Settings::values.program_args), "");
687 qt_config->setValue("dump_exefs", Settings::values.dump_exefs); 700 WriteSetting("dump_exefs", Settings::values.dump_exefs, false);
688 qt_config->setValue("dump_nso", Settings::values.dump_nso); 701 WriteSetting("dump_nso", Settings::values.dump_nso, false);
689 qt_config->endGroup(); 702 qt_config->endGroup();
690 703
691 qt_config->beginGroup("WebService"); 704 qt_config->beginGroup("WebService");
692 qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry); 705 WriteSetting("enable_telemetry", Settings::values.enable_telemetry, true);
693 qt_config->setValue("web_api_url", QString::fromStdString(Settings::values.web_api_url)); 706 WriteSetting("web_api_url", QString::fromStdString(Settings::values.web_api_url),
694 qt_config->setValue("yuzu_username", QString::fromStdString(Settings::values.yuzu_username)); 707 "https://api.yuzu-emu.org");
695 qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); 708 WriteSetting("yuzu_username", QString::fromStdString(Settings::values.yuzu_username));
709 WriteSetting("yuzu_token", QString::fromStdString(Settings::values.yuzu_token));
696 qt_config->endGroup(); 710 qt_config->endGroup();
697 711
698 qt_config->beginWriteArray("DisabledAddOns"); 712 qt_config->beginWriteArray("DisabledAddOns");
699 int i = 0; 713 int i = 0;
700 for (const auto& elem : Settings::values.disabled_addons) { 714 for (const auto& elem : Settings::values.disabled_addons) {
701 qt_config->setArrayIndex(i); 715 qt_config->setArrayIndex(i);
702 qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first)); 716 WriteSetting("title_id", QVariant::fromValue<u64>(elem.first), 0);
703 qt_config->beginWriteArray("disabled"); 717 qt_config->beginWriteArray("disabled");
704 for (std::size_t j = 0; j < elem.second.size(); ++j) { 718 for (std::size_t j = 0; j < elem.second.size(); ++j) {
705 qt_config->setArrayIndex(static_cast<int>(j)); 719 qt_config->setArrayIndex(static_cast<int>(j));
706 qt_config->setValue("d", QString::fromStdString(elem.second[j])); 720 WriteSetting("d", QString::fromStdString(elem.second[j]), "");
707 } 721 }
708 qt_config->endArray(); 722 qt_config->endArray();
709 ++i; 723 ++i;
@@ -711,60 +725,86 @@ void Config::SaveValues() {
711 qt_config->endArray(); 725 qt_config->endArray();
712 726
713 qt_config->beginGroup("UI"); 727 qt_config->beginGroup("UI");
714 qt_config->setValue("theme", UISettings::values.theme); 728 WriteSetting("theme", UISettings::values.theme, UISettings::themes[0].second);
715 qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); 729 WriteSetting("enable_discord_presence", UISettings::values.enable_discord_presence, true);
716 qt_config->setValue("screenshot_resolution_factor", 730 WriteSetting("screenshot_resolution_factor", UISettings::values.screenshot_resolution_factor,
717 UISettings::values.screenshot_resolution_factor); 731 0);
718 qt_config->setValue("select_user_on_boot", UISettings::values.select_user_on_boot); 732 WriteSetting("select_user_on_boot", UISettings::values.select_user_on_boot, false);
719 733
720 qt_config->beginGroup("UIGameList"); 734 qt_config->beginGroup("UIGameList");
721 qt_config->setValue("show_unknown", UISettings::values.show_unknown); 735 WriteSetting("show_unknown", UISettings::values.show_unknown, true);
722 qt_config->setValue("show_add_ons", UISettings::values.show_add_ons); 736 WriteSetting("show_add_ons", UISettings::values.show_add_ons, true);
723 qt_config->setValue("icon_size", UISettings::values.icon_size); 737 WriteSetting("icon_size", UISettings::values.icon_size, 64);
724 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id); 738 WriteSetting("row_1_text_id", UISettings::values.row_1_text_id, 3);
725 qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id); 739 WriteSetting("row_2_text_id", UISettings::values.row_2_text_id, 2);
726 qt_config->endGroup(); 740 qt_config->endGroup();
727 741
728 qt_config->beginGroup("UILayout"); 742 qt_config->beginGroup("UILayout");
729 qt_config->setValue("geometry", UISettings::values.geometry); 743 WriteSetting("geometry", UISettings::values.geometry);
730 qt_config->setValue("state", UISettings::values.state); 744 WriteSetting("state", UISettings::values.state);
731 qt_config->setValue("geometryRenderWindow", UISettings::values.renderwindow_geometry); 745 WriteSetting("geometryRenderWindow", UISettings::values.renderwindow_geometry);
732 qt_config->setValue("gameListHeaderState", UISettings::values.gamelist_header_state); 746 WriteSetting("gameListHeaderState", UISettings::values.gamelist_header_state);
733 qt_config->setValue("microProfileDialogGeometry", UISettings::values.microprofile_geometry); 747 WriteSetting("microProfileDialogGeometry", UISettings::values.microprofile_geometry);
734 qt_config->setValue("microProfileDialogVisible", UISettings::values.microprofile_visible); 748 WriteSetting("microProfileDialogVisible", UISettings::values.microprofile_visible, false);
735 qt_config->endGroup(); 749 qt_config->endGroup();
736 750
737 qt_config->beginGroup("Paths"); 751 qt_config->beginGroup("Paths");
738 qt_config->setValue("romsPath", UISettings::values.roms_path); 752 WriteSetting("romsPath", UISettings::values.roms_path);
739 qt_config->setValue("symbolsPath", UISettings::values.symbols_path); 753 WriteSetting("symbolsPath", UISettings::values.symbols_path);
740 qt_config->setValue("screenshotPath", UISettings::values.screenshot_path); 754 WriteSetting("screenshotPath", UISettings::values.screenshot_path);
741 qt_config->setValue("gameListRootDir", UISettings::values.gamedir); 755 WriteSetting("gameListRootDir", UISettings::values.gamedir, ".");
742 qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan); 756 WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false);
743 qt_config->setValue("recentFiles", UISettings::values.recent_files); 757 WriteSetting("recentFiles", UISettings::values.recent_files);
744 qt_config->endGroup(); 758 qt_config->endGroup();
745 759
746 qt_config->beginGroup("Shortcuts"); 760 qt_config->beginGroup("Shortcuts");
747 for (auto shortcut : UISettings::values.shortcuts) { 761 for (auto shortcut : UISettings::values.shortcuts) {
748 qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first); 762 WriteSetting(shortcut.first + "/KeySeq", shortcut.second.first);
749 qt_config->setValue(shortcut.first + "/Context", shortcut.second.second); 763 WriteSetting(shortcut.first + "/Context", shortcut.second.second);
750 } 764 }
751 qt_config->endGroup(); 765 qt_config->endGroup();
752 766
753 qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode); 767 WriteSetting("singleWindowMode", UISettings::values.single_window_mode, true);
754 qt_config->setValue("fullscreen", UISettings::values.fullscreen); 768 WriteSetting("fullscreen", UISettings::values.fullscreen, false);
755 qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar); 769 WriteSetting("displayTitleBars", UISettings::values.display_titlebar, true);
756 qt_config->setValue("showFilterBar", UISettings::values.show_filter_bar); 770 WriteSetting("showFilterBar", UISettings::values.show_filter_bar, true);
757 qt_config->setValue("showStatusBar", UISettings::values.show_status_bar); 771 WriteSetting("showStatusBar", UISettings::values.show_status_bar, true);
758 qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing); 772 WriteSetting("confirmClose", UISettings::values.confirm_before_closing, true);
759 qt_config->setValue("firstStart", UISettings::values.first_start); 773 WriteSetting("firstStart", UISettings::values.first_start, true);
760 qt_config->setValue("calloutFlags", UISettings::values.callout_flags); 774 WriteSetting("calloutFlags", UISettings::values.callout_flags, 0);
761 qt_config->setValue("showConsole", UISettings::values.show_console); 775 WriteSetting("showConsole", UISettings::values.show_console, false);
762 qt_config->setValue("profileIndex", UISettings::values.profile_index); 776 WriteSetting("profileIndex", UISettings::values.profile_index, 0);
763 qt_config->endGroup(); 777 qt_config->endGroup();
764} 778}
765 779
780QVariant Config::ReadSetting(const QString& name) const {
781 return qt_config->value(name);
782}
783
784QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const {
785 QVariant result;
786 if (qt_config->value(name + "/default", false).toBool()) {
787 result = default_value;
788 } else {
789 result = qt_config->value(name, default_value);
790 }
791 return result;
792}
793
794void Config::WriteSetting(const QString& name, const QVariant& value) {
795 qt_config->setValue(name, value);
796}
797
798void Config::WriteSetting(const QString& name, const QVariant& value,
799 const QVariant& default_value) {
800 qt_config->setValue(name + "/default", value == default_value);
801 qt_config->setValue(name, value);
802}
803
766void Config::Reload() { 804void Config::Reload() {
767 ReadValues(); 805 ReadValues();
806 // To apply default value changes
807 SaveValues();
768 Settings::Apply(); 808 Settings::Apply();
769} 809}
770 810
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index e73ad19bb..f4185db18 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -42,6 +42,11 @@ private:
42 void SaveMouseValues(); 42 void SaveMouseValues();
43 void SaveTouchscreenValues(); 43 void SaveTouchscreenValues();
44 44
45 QVariant ReadSetting(const QString& name) const;
46 QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
47 void WriteSetting(const QString& name, const QVariant& value);
48 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
49
45 std::unique_ptr<QSettings> qt_config; 50 std::unique_ptr<QSettings> qt_config;
46 std::string qt_config_loc; 51 std::string qt_config_loc;
47}; 52};
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 4116b6cd7..389fcf667 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -33,7 +33,6 @@ void ConfigureGeneral::setConfiguration() {
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 ui->enable_nfc->setChecked(Settings::values.enable_nfc);
37} 36}
38 37
39void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { 38void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
@@ -48,5 +47,4 @@ void ConfigureGeneral::applyConfiguration() {
48 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 47 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
49 48
50 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); 49 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
51 Settings::values.enable_nfc = ui->enable_nfc->isChecked();
52} 50}
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index dff0ad5d0..01d1c0b8e 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -71,26 +71,6 @@
71 </widget> 71 </widget>
72 </item> 72 </item>
73 <item> 73 <item>
74 <widget class="QGroupBox" name="EmulationGroupBox">
75 <property name="title">
76 <string>Emulation</string>
77 </property>
78 <layout class="QHBoxLayout" name="EmulationHorizontalLayout">
79 <item>
80 <layout class="QVBoxLayout" name="EmulationVerticalLayout">
81 <item>
82 <widget class="QCheckBox" name="enable_nfc">
83 <property name="text">
84 <string>Enable NFC</string>
85 </property>
86 </widget>
87 </item>
88 </layout>
89 </item>
90 </layout>
91 </widget>
92 </item>
93 <item>
94 <widget class="QGroupBox" name="theme_group_box"> 74 <widget class="QGroupBox" name="theme_group_box">
95 <property name="title"> 75 <property name="title">
96 <string>Theme</string> 76 <string>Theme</string>
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 71683da8e..f2d14becf 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -7,6 +7,7 @@
7#include <QDebug> 7#include <QDebug>
8#include <QFileDialog> 8#include <QFileDialog>
9#include <QLabel> 9#include <QLabel>
10#include <QMessageBox>
10#include <QMouseEvent> 11#include <QMouseEvent>
11#include <QPushButton> 12#include <QPushButton>
12#include <QScrollArea> 13#include <QScrollArea>
@@ -95,50 +96,91 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext
95 surface_picker_y_control = new QSpinBox; 96 surface_picker_y_control = new QSpinBox;
96 surface_picker_y_control->setRange(0, max_dimension - 1); 97 surface_picker_y_control->setRange(0, max_dimension - 1);
97 98
98 surface_format_control = new QComboBox; 99 // clang-format off
99
100 // Color formats sorted by Maxwell texture format index 100 // Color formats sorted by Maxwell texture format index
101 surface_format_control->addItem(tr("None")); 101 const QStringList surface_formats{
102 surface_format_control->addItem(tr("Unknown")); 102 tr("None"),
103 surface_format_control->addItem(tr("Unknown")); 103 QStringLiteral("R32_G32_B32_A32"),
104 surface_format_control->addItem(tr("Unknown")); 104 QStringLiteral("R32_G32_B32"),
105 surface_format_control->addItem(tr("Unknown")); 105 QStringLiteral("R16_G16_B16_A16"),
106 surface_format_control->addItem(tr("Unknown")); 106 QStringLiteral("R32_G32"),
107 surface_format_control->addItem(tr("Unknown")); 107 QStringLiteral("R32_B24G8"),
108 surface_format_control->addItem(tr("Unknown")); 108 QStringLiteral("ETC2_RGB"),
109 surface_format_control->addItem(tr("A8R8G8B8")); 109 QStringLiteral("X8B8G8R8"),
110 surface_format_control->addItem(tr("Unknown")); 110 QStringLiteral("A8R8G8B8"),
111 surface_format_control->addItem(tr("Unknown")); 111 QStringLiteral("A2B10G10R10"),
112 surface_format_control->addItem(tr("Unknown")); 112 QStringLiteral("ETC2_RGB_PTA"),
113 surface_format_control->addItem(tr("Unknown")); 113 QStringLiteral("ETC2_RGBA"),
114 surface_format_control->addItem(tr("Unknown")); 114 QStringLiteral("R16_G16"),
115 surface_format_control->addItem(tr("Unknown")); 115 QStringLiteral("G8R24"),
116 surface_format_control->addItem(tr("Unknown")); 116 QStringLiteral("G24R8"),
117 surface_format_control->addItem(tr("Unknown")); 117 QStringLiteral("R32"),
118 surface_format_control->addItem(tr("Unknown")); 118 QStringLiteral("BC6H_SF16"),
119 surface_format_control->addItem(tr("Unknown")); 119 QStringLiteral("BC6H_UF16"),
120 surface_format_control->addItem(tr("Unknown")); 120 QStringLiteral("A4B4G4R4"),
121 surface_format_control->addItem(tr("Unknown")); 121 QStringLiteral("A5B5G5R1"),
122 surface_format_control->addItem(tr("Unknown")); 122 QStringLiteral("A1B5G5R5"),
123 surface_format_control->addItem(tr("Unknown")); 123 QStringLiteral("B5G6R5"),
124 surface_format_control->addItem(tr("Unknown")); 124 QStringLiteral("B6G5R5"),
125 surface_format_control->addItem(tr("Unknown")); 125 QStringLiteral("BC7U"),
126 surface_format_control->addItem(tr("Unknown")); 126 QStringLiteral("G8R8"),
127 surface_format_control->addItem(tr("Unknown")); 127 QStringLiteral("EAC"),
128 surface_format_control->addItem(tr("Unknown")); 128 QStringLiteral("EACX2"),
129 surface_format_control->addItem(tr("Unknown")); 129 QStringLiteral("R16"),
130 surface_format_control->addItem(tr("Unknown")); 130 QStringLiteral("Y8_VIDEO"),
131 surface_format_control->addItem(tr("Unknown")); 131 QStringLiteral("R8"),
132 surface_format_control->addItem(tr("Unknown")); 132 QStringLiteral("G4R4"),
133 surface_format_control->addItem(tr("Unknown")); 133 QStringLiteral("R1"),
134 surface_format_control->addItem(tr("Unknown")); 134 QStringLiteral("E5B9G9R9_SHAREDEXP"),
135 surface_format_control->addItem(tr("Unknown")); 135 QStringLiteral("BF10GF11RF11"),
136 surface_format_control->addItem(tr("Unknown")); 136 QStringLiteral("G8B8G8R8"),
137 surface_format_control->addItem(tr("DXT1")); 137 QStringLiteral("B8G8R8G8"),
138 surface_format_control->addItem(tr("DXT23")); 138 QStringLiteral("DXT1"),
139 surface_format_control->addItem(tr("DXT45")); 139 QStringLiteral("DXT23"),
140 surface_format_control->addItem(tr("DXN1")); 140 QStringLiteral("DXT45"),
141 surface_format_control->addItem(tr("DXN2")); 141 QStringLiteral("DXN1"),
142 QStringLiteral("DXN2"),
143 QStringLiteral("Z24S8"),
144 QStringLiteral("X8Z24"),
145 QStringLiteral("S8Z24"),
146 QStringLiteral("X4V4Z24__COV4R4V"),
147 QStringLiteral("X4V4Z24__COV8R8V"),
148 QStringLiteral("V8Z24__COV4R12V"),
149 QStringLiteral("ZF32"),
150 QStringLiteral("ZF32_X24S8"),
151 QStringLiteral("X8Z24_X20V4S8__COV4R4V"),
152 QStringLiteral("X8Z24_X20V4S8__COV8R8V"),
153 QStringLiteral("ZF32_X20V4X8__COV4R4V"),
154 QStringLiteral("ZF32_X20V4X8__COV8R8V"),
155 QStringLiteral("ZF32_X20V4S8__COV4R4V"),
156 QStringLiteral("ZF32_X20V4S8__COV8R8V"),
157 QStringLiteral("X8Z24_X16V8S8__COV4R12V"),
158 QStringLiteral("ZF32_X16V8X8__COV4R12V"),
159 QStringLiteral("ZF32_X16V8S8__COV4R12V"),
160 QStringLiteral("Z16"),
161 QStringLiteral("V8Z24__COV8R24V"),
162 QStringLiteral("X8Z24_X16V8S8__COV8R24V"),
163 QStringLiteral("ZF32_X16V8X8__COV8R24V"),
164 QStringLiteral("ZF32_X16V8S8__COV8R24V"),
165 QStringLiteral("ASTC_2D_4X4"),
166 QStringLiteral("ASTC_2D_5X5"),
167 QStringLiteral("ASTC_2D_6X6"),
168 QStringLiteral("ASTC_2D_8X8"),
169 QStringLiteral("ASTC_2D_10X10"),
170 QStringLiteral("ASTC_2D_12X12"),
171 QStringLiteral("ASTC_2D_5X4"),
172 QStringLiteral("ASTC_2D_6X5"),
173 QStringLiteral("ASTC_2D_8X6"),
174 QStringLiteral("ASTC_2D_10X8"),
175 QStringLiteral("ASTC_2D_12X10"),
176 QStringLiteral("ASTC_2D_8X5"),
177 QStringLiteral("ASTC_2D_10X5"),
178 QStringLiteral("ASTC_2D_10X6"),
179 };
180 // clang-format on
181
182 surface_format_control = new QComboBox;
183 surface_format_control->addItems(surface_formats);
142 184
143 surface_info_label = new QLabel(); 185 surface_info_label = new QLabel();
144 surface_info_label->setWordWrap(true); 186 surface_info_label->setWordWrap(true);
@@ -157,22 +199,20 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext
157 199
158 // Connections 200 // Connections
159 connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate); 201 connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate);
160 connect(surface_source_list, 202 connect(surface_source_list, qOverload<int>(&QComboBox::currentIndexChanged), this,
161 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
162 &GraphicsSurfaceWidget::OnSurfaceSourceChanged); 203 &GraphicsSurfaceWidget::OnSurfaceSourceChanged);
163 connect(surface_address_control, &CSpinBox::ValueChanged, this, 204 connect(surface_address_control, &CSpinBox::ValueChanged, this,
164 &GraphicsSurfaceWidget::OnSurfaceAddressChanged); 205 &GraphicsSurfaceWidget::OnSurfaceAddressChanged);
165 connect(surface_width_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 206 connect(surface_width_control, qOverload<int>(&QSpinBox::valueChanged), this,
166 this, &GraphicsSurfaceWidget::OnSurfaceWidthChanged); 207 &GraphicsSurfaceWidget::OnSurfaceWidthChanged);
167 connect(surface_height_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 208 connect(surface_height_control, qOverload<int>(&QSpinBox::valueChanged), this,
168 this, &GraphicsSurfaceWidget::OnSurfaceHeightChanged); 209 &GraphicsSurfaceWidget::OnSurfaceHeightChanged);
169 connect(surface_format_control, 210 connect(surface_format_control, qOverload<int>(&QComboBox::currentIndexChanged), this,
170 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
171 &GraphicsSurfaceWidget::OnSurfaceFormatChanged); 211 &GraphicsSurfaceWidget::OnSurfaceFormatChanged);
172 connect(surface_picker_x_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 212 connect(surface_picker_x_control, qOverload<int>(&QSpinBox::valueChanged), this,
173 this, &GraphicsSurfaceWidget::OnSurfacePickerXChanged); 213 &GraphicsSurfaceWidget::OnSurfacePickerXChanged);
174 connect(surface_picker_y_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 214 connect(surface_picker_y_control, qOverload<int>(&QSpinBox::valueChanged), this,
175 this, &GraphicsSurfaceWidget::OnSurfacePickerYChanged); 215 &GraphicsSurfaceWidget::OnSurfacePickerYChanged);
176 connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface); 216 connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface);
177 217
178 auto main_widget = new QWidget; 218 auto main_widget = new QWidget;
@@ -261,7 +301,7 @@ void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
261 301
262void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { 302void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
263 if (surface_address != new_value) { 303 if (surface_address != new_value) {
264 surface_address = static_cast<Tegra::GPUVAddr>(new_value); 304 surface_address = static_cast<GPUVAddr>(new_value);
265 305
266 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); 306 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
267 emit Update(); 307 emit Update();
@@ -383,13 +423,12 @@ void GraphicsSurfaceWidget::OnUpdate() {
383 // TODO: Implement a good way to visualize alpha components! 423 // TODO: Implement a good way to visualize alpha components!
384 424
385 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); 425 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
386 std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
387 426
388 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. 427 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
389 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. 428 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
390 auto unswizzled_data = Tegra::Texture::UnswizzleTexture( 429 auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
391 *address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, 430 gpu.MemoryManager().GetPointer(surface_address), 1, 1,
392 surface_height, 1U); 431 Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U);
393 432
394 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 433 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
395 surface_width, surface_height); 434 surface_width, surface_height);
@@ -421,40 +460,56 @@ void GraphicsSurfaceWidget::OnUpdate() {
421} 460}
422 461
423void GraphicsSurfaceWidget::SaveSurface() { 462void GraphicsSurfaceWidget::SaveSurface() {
424 QString png_filter = tr("Portable Network Graphic (*.png)"); 463 const QString png_filter = tr("Portable Network Graphic (*.png)");
425 QString bin_filter = tr("Binary data (*.bin)"); 464 const QString bin_filter = tr("Binary data (*.bin)");
426 465
427 QString selectedFilter; 466 QString selected_filter;
428 QString filename = QFileDialog::getSaveFileName( 467 const QString filename = QFileDialog::getSaveFileName(
429 this, tr("Save Surface"), 468 this, tr("Save Surface"),
430 QString("texture-0x%1.png").arg(QString::number(surface_address, 16)), 469 QStringLiteral("texture-0x%1.png").arg(QString::number(surface_address, 16)),
431 QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter); 470 QStringLiteral("%1;;%2").arg(png_filter, bin_filter), &selected_filter);
432 471
433 if (filename.isEmpty()) { 472 if (filename.isEmpty()) {
434 // If the user canceled the dialog, don't save anything. 473 // If the user canceled the dialog, don't save anything.
435 return; 474 return;
436 } 475 }
437 476
438 if (selectedFilter == png_filter) { 477 if (selected_filter == png_filter) {
439 const QPixmap* pixmap = surface_picture_label->pixmap(); 478 const QPixmap* const pixmap = surface_picture_label->pixmap();
440 ASSERT_MSG(pixmap != nullptr, "No pixmap set"); 479 ASSERT_MSG(pixmap != nullptr, "No pixmap set");
441 480
442 QFile file(filename); 481 QFile file{filename};
443 file.open(QIODevice::WriteOnly); 482 if (!file.open(QIODevice::WriteOnly)) {
444 if (pixmap) 483 QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename));
445 pixmap->save(&file, "PNG"); 484 return;
446 } else if (selectedFilter == bin_filter) { 485 }
486
487 if (!pixmap->save(&file, "PNG")) {
488 QMessageBox::warning(this, tr("Error"),
489 tr("Failed to save surface data to file '%1'").arg(filename));
490 }
491 } else if (selected_filter == bin_filter) {
447 auto& gpu = Core::System::GetInstance().GPU(); 492 auto& gpu = Core::System::GetInstance().GPU();
448 std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); 493 const std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
449 494
450 const u8* buffer = Memory::GetPointer(*address); 495 const u8* const buffer = Memory::GetPointer(*address);
451 ASSERT_MSG(buffer != nullptr, "Memory not accessible"); 496 ASSERT_MSG(buffer != nullptr, "Memory not accessible");
452 497
453 QFile file(filename); 498 QFile file{filename};
454 file.open(QIODevice::WriteOnly); 499 if (!file.open(QIODevice::WriteOnly)) {
455 int size = surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format); 500 QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename));
456 QByteArray data(reinterpret_cast<const char*>(buffer), size); 501 return;
457 file.write(data); 502 }
503
504 const int size =
505 surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format);
506 const QByteArray data(reinterpret_cast<const char*>(buffer), size);
507 if (file.write(data) != data.size()) {
508 QMessageBox::warning(
509 this, tr("Error"),
510 tr("Failed to completely write surface data to file. The saved data will "
511 "likely be corrupt."));
512 }
458 } else { 513 } else {
459 UNREACHABLE_MSG("Unhandled filter selected"); 514 UNREACHABLE_MSG("Unhandled filter selected");
460 } 515 }
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h
index 323e39d94..89445b18f 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.h
+++ b/src/yuzu/debugger/graphics/graphics_surface.h
@@ -87,7 +87,7 @@ private:
87 QPushButton* save_surface; 87 QPushButton* save_surface;
88 88
89 Source surface_source; 89 Source surface_source;
90 Tegra::GPUVAddr surface_address; 90 GPUVAddr surface_address;
91 unsigned surface_width; 91 unsigned surface_width;
92 unsigned surface_height; 92 unsigned surface_height;
93 Tegra::Texture::TextureFormat surface_format; 93 Tegra::Texture::TextureFormat surface_format;
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index 8b30e0a85..86e03e46d 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -7,6 +7,7 @@
7#include <QMouseEvent> 7#include <QMouseEvent>
8#include <QPainter> 8#include <QPainter>
9#include <QString> 9#include <QString>
10#include <QTimer>
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "common/microprofile.h" 12#include "common/microprofile.h"
12#include "yuzu/debugger/profiler.h" 13#include "yuzu/debugger/profiler.h"
diff --git a/src/yuzu/debugger/profiler.h b/src/yuzu/debugger/profiler.h
index eae1e9e3c..8e69fdb06 100644
--- a/src/yuzu/debugger/profiler.h
+++ b/src/yuzu/debugger/profiler.h
@@ -4,10 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <QAbstractItemModel> 7#include <QWidget>
8#include <QDockWidget> 8
9#include <QTimer> 9class QAction;
10#include "common/microprofile.h" 10class QHideEvent;
11class QShowEvent;
11 12
12class MicroProfileDialog : public QWidget { 13class MicroProfileDialog : public QWidget {
13 Q_OBJECT 14 Q_OBJECT
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 06ad74ffe..593bb681f 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -234,6 +234,9 @@ QString WaitTreeThread::GetText() const {
234 case Kernel::ThreadStatus::WaitMutex: 234 case Kernel::ThreadStatus::WaitMutex:
235 status = tr("waiting for mutex"); 235 status = tr("waiting for mutex");
236 break; 236 break;
237 case Kernel::ThreadStatus::WaitCondVar:
238 status = tr("waiting for condition variable");
239 break;
237 case Kernel::ThreadStatus::WaitArb: 240 case Kernel::ThreadStatus::WaitArb:
238 status = tr("waiting for address arbiter"); 241 status = tr("waiting for address arbiter");
239 break; 242 break;
@@ -269,6 +272,7 @@ QColor WaitTreeThread::GetColor() const {
269 case Kernel::ThreadStatus::WaitSynchAll: 272 case Kernel::ThreadStatus::WaitSynchAll:
270 case Kernel::ThreadStatus::WaitSynchAny: 273 case Kernel::ThreadStatus::WaitSynchAny:
271 case Kernel::ThreadStatus::WaitMutex: 274 case Kernel::ThreadStatus::WaitMutex:
275 case Kernel::ThreadStatus::WaitCondVar:
272 case Kernel::ThreadStatus::WaitArb: 276 case Kernel::ThreadStatus::WaitArb:
273 return QColor(Qt::GlobalColor::red); 277 return QColor(Qt::GlobalColor::red);
274 case Kernel::ThreadStatus::Dormant: 278 case Kernel::ThreadStatus::Dormant:
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index c0e3c5fa9..4422a572b 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -329,6 +329,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
329 QMenu context_menu; 329 QMenu context_menu;
330 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); 330 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")); 331 QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location"));
332 QAction* open_transferable_shader_cache =
333 context_menu.addAction(tr("Open Transferable Shader Cache"));
332 context_menu.addSeparator(); 334 context_menu.addSeparator();
333 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); 335 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
334 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 336 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
@@ -344,6 +346,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
344 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); 346 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); });
345 connect(open_lfs_location, &QAction::triggered, 347 connect(open_lfs_location, &QAction::triggered,
346 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); }); 348 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); });
349 connect(open_transferable_shader_cache, &QAction::triggered,
350 [&]() { emit OpenTransferableShaderCacheRequested(program_id); });
347 connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); }); 351 connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); });
348 connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); 352 connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); });
349 connect(navigate_to_gamedb_entry, &QAction::triggered, 353 connect(navigate_to_gamedb_entry, &QAction::triggered,
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index b317eb2fc..8ea5cbaaa 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -66,6 +66,7 @@ signals:
66 void GameChosen(QString game_path); 66 void GameChosen(QString game_path);
67 void ShouldCancelWorker(); 67 void ShouldCancelWorker();
68 void OpenFolderRequested(u64 program_id, GameListOpenTarget target); 68 void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
69 void OpenTransferableShaderCacheRequested(u64 program_id);
69 void DumpRomFSRequested(u64 program_id, const std::string& game_path); 70 void DumpRomFSRequested(u64 program_id, const std::string& game_path);
70 void CopyTIDRequested(u64 program_id); 71 void CopyTIDRequested(u64 program_id);
71 void NavigateToGamedbEntryRequested(u64 program_id, 72 void NavigateToGamedbEntryRequested(u64 program_id,
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 41ba3c4c6..2b9db69a3 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"
@@ -648,6 +651,8 @@ void GMainWindow::RestoreUIState() {
648void GMainWindow::ConnectWidgetEvents() { 651void GMainWindow::ConnectWidgetEvents() {
649 connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); 652 connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile);
650 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); 653 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
654 connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this,
655 &GMainWindow::OnTransferableShaderCacheOpenFile);
651 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); 656 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
652 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); 657 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
653 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, 658 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
@@ -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
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index e07c892cf..7f3aa998e 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -176,6 +176,7 @@ private slots:
176 /// Called whenever a user selects a game in the game list widget. 176 /// Called whenever a user selects a game in the game list widget.
177 void OnGameListLoadFile(QString game_path); 177 void OnGameListLoadFile(QString game_path);
178 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); 178 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
179 void OnTransferableShaderCacheOpenFile(u64 program_id);
179 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); 180 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
180 void OnGameListCopyTID(u64 program_id); 181 void OnGameListCopyTID(u64 program_id);
181 void OnGameListNavigateToGamedbEntry(u64 program_id, 182 void OnGameListNavigateToGamedbEntry(u64 program_id,
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 32e78049c..f24cc77fe 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -319,7 +319,6 @@ void Config::ReadValues() {
319 319
320 // System 320 // System
321 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); 321 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
322 Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true);
323 const auto size = sdl2_config->GetInteger("System", "users_size", 0); 322 const auto size = sdl2_config->GetInteger("System", "users_size", 0);
324 323
325 Settings::values.current_user = std::clamp<int>( 324 Settings::values.current_user = std::clamp<int>(
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c6c66a787..245f25847 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -114,9 +114,9 @@ int main(int argc, char** argv) {
114 }; 114 };
115 115
116 while (optind < argc) { 116 while (optind < argc) {
117 char arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index); 117 int arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index);
118 if (arg != -1) { 118 if (arg != -1) {
119 switch (arg) { 119 switch (static_cast<char>(arg)) {
120 case 'g': 120 case 'g':
121 errno = 0; 121 errno = 0;
122 gdb_port = strtoul(optarg, &endarg, 0); 122 gdb_port = strtoul(optarg, &endarg, 0);