summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/bit_field.h12
-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/page_table.cpp29
-rw-r--r--src/common/page_table.h80
-rw-r--r--src/common/swap.h174
-rw-r--r--src/common/thread_queue_list.h6
-rw-r--r--src/common/uint128.cpp45
-rw-r--r--src/common/uint128.h19
-rw-r--r--src/core/CMakeLists.txt8
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/core.h4
-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.cpp493
-rw-r--r--src/core/file_sys/cheat_engine.h227
-rw-r--r--src/core/file_sys/content_archive.h15
-rw-r--r--src/core/file_sys/patch_manager.cpp54
-rw-r--r--src/core/file_sys/patch_manager.h4
-rw-r--r--src/core/file_sys/registered_cache.cpp2
-rw-r--r--src/core/hle/ipc.h44
-rw-r--r--src/core/hle/ipc_helpers.h30
-rw-r--r--src/core/hle/kernel/code_set.cpp12
-rw-r--r--src/core/hle/kernel/code_set.h90
-rw-r--r--src/core/hle/kernel/process.cpp14
-rw-r--r--src/core/hle/kernel/process.h43
-rw-r--r--src/core/hle/kernel/scheduler.cpp8
-rw-r--r--src/core/hle/kernel/svc.cpp32
-rw-r--r--src/core/hle/kernel/thread.cpp72
-rw-r--r--src/core/hle/kernel/thread.h23
-rw-r--r--src/core/hle/kernel/vm_manager.cpp26
-rw-r--r--src/core/hle/kernel/vm_manager.h20
-rw-r--r--src/core/hle/service/am/am.cpp86
-rw-r--r--src/core/hle/service/am/am.h16
-rw-r--r--src/core/hle/service/audio/hwopus.cpp82
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp5
-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.h3
-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/nvdrv/devices/nvdevice.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp3
-rw-r--r--src/core/loader/elf.cpp1
-rw-r--r--src/core/loader/linker.cpp147
-rw-r--r--src/core/loader/linker.h36
-rw-r--r--src/core/loader/nro.cpp1
-rw-r--r--src/core/loader/nro.h4
-rw-r--r--src/core/loader/nso.cpp13
-rw-r--r--src/core/loader/nso.h4
-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/input_common/sdl/sdl.h12
-rw-r--r--src/input_common/sdl/sdl_impl.cpp14
-rw-r--r--src/input_common/sdl/sdl_impl.h5
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/bit_field.cpp90
-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.txt2
-rw-r--r--src/video_core/dma_pusher.cpp7
-rw-r--r--src/video_core/engines/kepler_memory.cpp15
-rw-r--r--src/video_core/engines/maxwell_3d.cpp42
-rw-r--r--src/video_core/engines/maxwell_dma.cpp27
-rw-r--r--src/video_core/gpu.cpp13
-rw-r--r--src/video_core/gpu.h11
-rw-r--r--src/video_core/gpu_asynch.cpp6
-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.cpp136
-rw-r--r--src/video_core/gpu_thread.h132
-rw-r--r--src/video_core/memory_manager.cpp55
-rw-r--r--src/video_core/memory_manager.h17
-rw-r--r--src/video_core/morton.cpp324
-rw-r--r--src/video_core/morton.h6
-rw-r--r--src/video_core/rasterizer_cache.h70
-rw-r--r--src/video_core/rasterizer_interface.h8
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp26
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h31
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.cpp41
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.h17
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp16
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp69
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h19
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp62
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h19
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp27
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h31
-rw-r--r--src/video_core/renderer_vulkan/vk_sampler_cache.cpp81
-rw-r--r--src/video_core/renderer_vulkan/vk_sampler_cache.h56
-rw-r--r--src/video_core/textures/decoders.cpp32
-rw-r--r--src/video_core/textures/decoders.h13
-rw-r--r--src/video_core/textures/texture.h53
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/configuration/config.cpp448
-rw-r--r--src/yuzu/configuration/config.h5
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp5
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
108 files changed, 2995 insertions, 1621 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 3d30f0e3e..43ae8a9e7 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -92,10 +92,14 @@ add_library(common STATIC
92 logging/text_formatter.cpp 92 logging/text_formatter.cpp
93 logging/text_formatter.h 93 logging/text_formatter.h
94 math_util.h 94 math_util.h
95 memory_hook.cpp
96 memory_hook.h
95 microprofile.cpp 97 microprofile.cpp
96 microprofile.h 98 microprofile.h
97 microprofileui.h 99 microprofileui.h
98 misc.cpp 100 misc.cpp
101 page_table.cpp
102 page_table.h
99 param_package.cpp 103 param_package.cpp
100 param_package.h 104 param_package.h
101 quaternion.h 105 quaternion.h
@@ -114,6 +118,8 @@ add_library(common STATIC
114 threadsafe_queue.h 118 threadsafe_queue.h
115 timer.cpp 119 timer.cpp
116 timer.h 120 timer.h
121 uint128.cpp
122 uint128.h
117 vector_math.h 123 vector_math.h
118 web_result.h 124 web_result.h
119) 125)
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/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/page_table.cpp b/src/common/page_table.cpp
new file mode 100644
index 000000000..8eba1c3f1
--- /dev/null
+++ b/src/common/page_table.cpp
@@ -0,0 +1,29 @@
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
20 // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
21 // vector size is subsequently decreased (via resize), the vector might not automatically
22 // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
23 // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
24
25 pointers.shrink_to_fit();
26 attributes.shrink_to_fit();
27}
28
29} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
new file mode 100644
index 000000000..8339f2890
--- /dev/null
+++ b/src/common/page_table.h
@@ -0,0 +1,80 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <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};
25
26struct SpecialRegion {
27 enum class Type {
28 DebugHook,
29 IODevice,
30 } type;
31
32 MemoryHookPointer handler;
33
34 bool operator<(const SpecialRegion& other) const {
35 return std::tie(type, handler) < std::tie(other.type, other.handler);
36 }
37
38 bool operator==(const SpecialRegion& other) const {
39 return std::tie(type, handler) == std::tie(other.type, other.handler);
40 }
41};
42
43/**
44 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
45 * mimics the way a real CPU page table works.
46 */
47struct PageTable {
48 explicit PageTable(std::size_t page_size_in_bits);
49 ~PageTable();
50
51 /**
52 * Resizes the page table to be able to accomodate enough pages within
53 * a given address space.
54 *
55 * @param address_space_width_in_bits The address size width in bits.
56 */
57 void Resize(std::size_t address_space_width_in_bits);
58
59 /**
60 * Vector of memory pointers backing each page. An entry can only be non-null if the
61 * corresponding entry in the `attributes` vector is of type `Memory`.
62 */
63 std::vector<u8*> pointers;
64
65 /**
66 * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
67 * of type `Special`.
68 */
69 boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
70
71 /**
72 * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
73 * the corresponding entry in `pointers` MUST be set to null.
74 */
75 std::vector<PageType> attributes;
76
77 const std::size_t page_size_in_bits{};
78};
79
80} // 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_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/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..bbbe60896 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
@@ -107,6 +109,8 @@ add_library(core STATIC
107 hle/kernel/client_port.h 109 hle/kernel/client_port.h
108 hle/kernel/client_session.cpp 110 hle/kernel/client_session.cpp
109 hle/kernel/client_session.h 111 hle/kernel/client_session.h
112 hle/kernel/code_set.cpp
113 hle/kernel/code_set.h
110 hle/kernel/errors.h 114 hle/kernel/errors.h
111 hle/kernel/handle_table.cpp 115 hle/kernel/handle_table.cpp
112 hle/kernel/handle_table.h 116 hle/kernel/handle_table.h
@@ -419,8 +423,6 @@ add_library(core STATIC
419 loader/deconstructed_rom_directory.h 423 loader/deconstructed_rom_directory.h
420 loader/elf.cpp 424 loader/elf.cpp
421 loader/elf.h 425 loader/elf.h
422 loader/linker.cpp
423 loader/linker.h
424 loader/loader.cpp 426 loader/loader.cpp
425 loader/loader.h 427 loader/loader.h
426 loader/nax.cpp 428 loader/nax.cpp
@@ -437,8 +439,6 @@ add_library(core STATIC
437 loader/xci.h 439 loader/xci.h
438 memory.cpp 440 memory.cpp
439 memory.h 441 memory.h
440 memory_hook.cpp
441 memory_hook.h
442 memory_setup.h 442 memory_setup.h
443 perf_stats.cpp 443 perf_stats.cpp
444 perf_stats.h 444 perf_stats.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 9b7ca4030..4fdc12f11 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"
@@ -119,7 +120,7 @@ public:
119 return std::max(parent.core_timing.GetDowncount(), 0); 120 return std::max(parent.core_timing.GetDowncount(), 0);
120 } 121 }
121 u64 GetCNTPCT() override { 122 u64 GetCNTPCT() override {
122 return parent.core_timing.GetTicks(); 123 return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks());
123 } 124 }
124 125
125 ARM_Dynarmic& parent; 126 ARM_Dynarmic& parent;
@@ -151,7 +152,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
151 config.tpidr_el0 = &cb->tpidr_el0; 152 config.tpidr_el0 = &cb->tpidr_el0;
152 config.dczid_el0 = 4; 153 config.dczid_el0 = 4;
153 config.ctr_el0 = 0x8444c004; 154 config.ctr_el0 = 0x8444c004;
154 config.cntfrq_el0 = 19200000; // Value from fusee. 155 config.cntfrq_el0 = Timing::CNTFREQ;
155 156
156 // Unpredictable instructions 157 // Unpredictable instructions
157 config.define_unpredictable_behaviour = true; 158 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..aada1e862 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
@@ -70,7 +70,7 @@ 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 {
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 89b3fb418..a88e332be 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 =
464 std::make_unique<FileSys::CheatEngine>(list, build_id, 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_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..09ca9d705
--- /dev/null
+++ b/src/core/file_sys/cheat_engine.cpp
@@ -0,0 +1,493 @@
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/controller_base.h"
15#include "core/hle/service/hid/controllers/npad.h"
16#include "core/hle/service/hid/hid.h"
17#include "core/hle/service/sm/sm.h"
18
19namespace FileSys {
20
21constexpr u64 CHEAT_ENGINE_TICKS = Core::Timing::BASE_CLOCK_RATE / 60;
22constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
23
24u64 Cheat::Address() const {
25 u64 out;
26 std::memcpy(&out, raw.data(), sizeof(u64));
27 return Common::swap64(out) & 0xFFFFFFFFFF;
28}
29
30u64 Cheat::ValueWidth(u64 offset) const {
31 return Value(offset, width);
32}
33
34u64 Cheat::Value(u64 offset, u64 width) const {
35 u64 out;
36 std::memcpy(&out, raw.data() + offset, sizeof(u64));
37 out = Common::swap64(out);
38 if (width == 8)
39 return out;
40 return out & ((1ull << (width * CHAR_BIT)) - 1);
41}
42
43u32 Cheat::KeypadValue() const {
44 u32 out;
45 std::memcpy(&out, raw.data(), sizeof(u32));
46 return Common::swap32(out) & 0x0FFFFFFF;
47}
48
49void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
50 VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
51 this->main_region_begin = main_begin;
52 this->main_region_end = main_end;
53 this->heap_region_begin = heap_begin;
54 this->heap_region_end = heap_end;
55 this->writer = writer;
56 this->reader = reader;
57}
58
59MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
60
61void CheatList::Execute() {
62 MICROPROFILE_SCOPE(Cheat_Engine);
63
64 std::fill(scratch.begin(), scratch.end(), 0);
65 in_standard = false;
66 for (std::size_t i = 0; i < master_list.size(); ++i) {
67 LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
68 current_block = i;
69 ExecuteBlock(master_list[i].second);
70 }
71
72 in_standard = true;
73 for (std::size_t i = 0; i < standard_list.size(); ++i) {
74 LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
75 current_block = i;
76 ExecuteBlock(standard_list[i].second);
77 }
78}
79
80CheatList::CheatList(ProgramSegment master, ProgramSegment standard)
81 : master_list(master), standard_list(standard) {}
82
83bool CheatList::EvaluateConditional(const Cheat& cheat) const {
84 using ComparisonFunction = bool (*)(u64, u64);
85 constexpr std::array<ComparisonFunction, 6> comparison_functions{
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 [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
89 };
90
91 if (cheat.type == CodeType::ConditionalInput) {
92 const auto applet_resource = Core::System::GetInstance()
93 .ServiceManager()
94 .GetService<Service::HID::Hid>("hid")
95 ->GetAppletResource();
96 if (applet_resource == nullptr) {
97 LOG_WARNING(
98 Common_Filesystem,
99 "Attempted to evaluate input conditional, but applet resource is not initialized!");
100 return false;
101 }
102
103 const auto press_state =
104 applet_resource
105 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
106 .GetAndResetPressState();
107 return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
108 }
109
110 ASSERT(cheat.type == CodeType::Conditional);
111
112 const auto offset =
113 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
114 ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
115 auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
116 const auto addr = cheat.Address() + offset;
117
118 return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
119}
120
121void CheatList::ProcessBlockPairs(const Block& block) {
122 block_pairs.clear();
123
124 u64 scope = 0;
125 std::map<u64, u64> pairs;
126
127 for (std::size_t i = 0; i < block.size(); ++i) {
128 const auto& cheat = block[i];
129
130 switch (cheat.type) {
131 case CodeType::Conditional:
132 case CodeType::ConditionalInput:
133 pairs.insert_or_assign(scope, i);
134 ++scope;
135 break;
136 case CodeType::EndConditional: {
137 --scope;
138 const auto idx = pairs.at(scope);
139 block_pairs.insert_or_assign(idx, i);
140 break;
141 }
142 case CodeType::Loop: {
143 if (cheat.end_of_loop) {
144 --scope;
145 const auto idx = pairs.at(scope);
146 block_pairs.insert_or_assign(idx, i);
147 } else {
148 pairs.insert_or_assign(scope, i);
149 ++scope;
150 }
151 break;
152 }
153 }
154 }
155}
156
157void CheatList::WriteImmediate(const Cheat& cheat) {
158 const auto offset =
159 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
160 const auto& register_3 = scratch.at(cheat.register_3);
161
162 const auto addr = cheat.Address() + offset + register_3;
163 LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
164 cheat.Value(8, cheat.width));
165 writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
166}
167
168void CheatList::BeginConditional(const Cheat& cheat) {
169 if (EvaluateConditional(cheat)) {
170 return;
171 }
172
173 const auto iter = block_pairs.find(current_index);
174 ASSERT(iter != block_pairs.end());
175 current_index = iter->second - 1;
176}
177
178void CheatList::EndConditional(const Cheat& cheat) {
179 LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
180}
181
182void CheatList::Loop(const Cheat& cheat) {
183 if (cheat.end_of_loop.Value())
184 ASSERT(!cheat.end_of_loop.Value());
185
186 auto& register_3 = scratch.at(cheat.register_3);
187 const auto iter = block_pairs.find(current_index);
188 ASSERT(iter != block_pairs.end());
189 ASSERT(iter->first < iter->second);
190
191 for (int i = cheat.Value(4, 4); i >= 0; --i) {
192 register_3 = i;
193 for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
194 current_index = c;
195 ExecuteSingleCheat(
196 (in_standard ? standard_list : master_list)[current_block].second[c]);
197 }
198 }
199
200 current_index = iter->second;
201}
202
203void CheatList::LoadImmediate(const Cheat& cheat) {
204 auto& register_3 = scratch.at(cheat.register_3);
205
206 LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
207 cheat.Value(4, 8));
208 register_3 = cheat.Value(4, 8);
209}
210
211void CheatList::LoadIndexed(const Cheat& cheat) {
212 const auto offset =
213 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
214 auto& register_3 = scratch.at(cheat.register_3);
215
216 const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
217 LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
218 cheat.register_3, addr);
219 register_3 = reader(cheat.width, SanitizeAddress(addr));
220}
221
222void CheatList::StoreIndexed(const Cheat& cheat) {
223 const auto& register_3 = scratch.at(cheat.register_3);
224
225 const auto addr =
226 register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
227 LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
228 cheat.Value(4, cheat.width), addr);
229 writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
230}
231
232void CheatList::RegisterArithmetic(const Cheat& cheat) {
233 using ArithmeticFunction = u64 (*)(u64, u64);
234 constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
235 [](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
236 [](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
237 [](u64 a, u64 b) { return a >> b; },
238 };
239
240 using ArithmeticOverflowCheck = bool (*)(u64, u64);
241 constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
242 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
243 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
244 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
245 [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
246 [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
247 };
248
249 static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
250 "Missing or have extra arithmetic overflow checks compared to functions!");
251
252 auto& register_3 = scratch.at(cheat.register_3);
253
254 ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
255 auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
256 auto* overflow_function =
257 arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
258 LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
259 cheat.register_3, cheat.ValueWidth(4));
260
261 if (overflow_function(register_3, cheat.ValueWidth(4))) {
262 LOG_WARNING(Common_Filesystem,
263 "overflow will occur when performing arithmetic operation={:02X} with operands "
264 "a={:016X}, b={:016X}!",
265 static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
266 }
267
268 register_3 = function(register_3, cheat.ValueWidth(4));
269}
270
271void CheatList::BeginConditionalInput(const Cheat& cheat) {
272 if (EvaluateConditional(cheat))
273 return;
274
275 const auto iter = block_pairs.find(current_index);
276 ASSERT(iter != block_pairs.end());
277 current_index = iter->second - 1;
278}
279
280VAddr CheatList::SanitizeAddress(VAddr in) const {
281 if ((in < main_region_begin || in >= main_region_end) &&
282 (in < heap_region_begin || in >= heap_region_end)) {
283 LOG_ERROR(Common_Filesystem,
284 "Cheat attempting to access memory at invalid address={:016X}, if this persists, "
285 "the cheat may be incorrect. However, this may be normal early in execution if "
286 "the game has not properly set up yet.",
287 in);
288 return 0; ///< Invalid addresses will hard crash
289 }
290
291 return in;
292}
293
294void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
295 using CheatOperationFunction = void (CheatList::*)(const Cheat&);
296 constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
297 &CheatList::WriteImmediate, &CheatList::BeginConditional,
298 &CheatList::EndConditional, &CheatList::Loop,
299 &CheatList::LoadImmediate, &CheatList::LoadIndexed,
300 &CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
301 &CheatList::BeginConditionalInput,
302 };
303
304 const auto index = static_cast<u8>(cheat.type.Value());
305 ASSERT(index < sizeof(cheat_operation_functions));
306 const auto op = cheat_operation_functions[index];
307 (this->*op)(cheat);
308}
309
310void CheatList::ExecuteBlock(const Block& block) {
311 encountered_loops.clear();
312
313 ProcessBlockPairs(block);
314 for (std::size_t i = 0; i < block.size(); ++i) {
315 current_index = i;
316 ExecuteSingleCheat(block[i]);
317 i = current_index;
318 }
319}
320
321CheatParser::~CheatParser() = default;
322
323CheatList CheatParser::MakeCheatList(CheatList::ProgramSegment master,
324 CheatList::ProgramSegment standard) const {
325 return {master, standard};
326}
327
328TextCheatParser::~TextCheatParser() = default;
329
330CheatList TextCheatParser::Parse(const std::vector<u8>& data) const {
331 std::stringstream ss;
332 ss.write(reinterpret_cast<const char*>(data.data()), data.size());
333
334 std::vector<std::string> lines;
335 std::string stream_line;
336 while (std::getline(ss, stream_line)) {
337 // Remove a trailing \r
338 if (!stream_line.empty() && stream_line.back() == '\r')
339 stream_line.pop_back();
340 lines.push_back(std::move(stream_line));
341 }
342
343 CheatList::ProgramSegment master_list;
344 CheatList::ProgramSegment standard_list;
345
346 for (std::size_t i = 0; i < lines.size(); ++i) {
347 auto line = lines[i];
348
349 if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
350 const auto master = line[0] == '{';
351 const auto begin = master ? line.find('{') : line.find('[');
352 const auto end = master ? line.rfind('}') : line.rfind(']');
353
354 ASSERT(begin != std::string::npos && end != std::string::npos);
355
356 const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
357 CheatList::Block block{};
358
359 while (i < lines.size() - 1) {
360 line = lines[++i];
361 if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
362 --i;
363 break;
364 }
365
366 if (line.size() < 8)
367 continue;
368
369 Cheat out{};
370 out.raw = ParseSingleLineCheat(line);
371 block.push_back(out);
372 }
373
374 (master ? master_list : standard_list).emplace_back(patch_name, block);
375 }
376 }
377
378 return MakeCheatList(master_list, standard_list);
379}
380
381std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
382 std::array<u8, 16> out{};
383
384 if (line.size() < 8)
385 return out;
386
387 const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
388 std::memcpy(out.data(), word1.data(), sizeof(u32));
389
390 if (line.size() < 17 || line[8] != ' ')
391 return out;
392
393 const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
394 std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
395
396 if (line.size() < 26 || line[17] != ' ') {
397 // Perform shifting in case value is truncated early.
398 const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
399 if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
400 type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
401 std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
402 std::memset(out.data() + 4, 0, sizeof(u32));
403 }
404
405 return out;
406 }
407
408 const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
409 std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
410
411 if (line.size() < 35 || line[26] != ' ') {
412 // Perform shifting in case value is truncated early.
413 const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
414 if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
415 std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
416 std::memset(out.data() + 8, 0, sizeof(u32));
417 }
418
419 return out;
420 }
421
422 const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
423 std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
424
425 return out;
426}
427
428u64 MemoryReadImpl(u32 width, VAddr addr) {
429 switch (width) {
430 case 1:
431 return Memory::Read8(addr);
432 case 2:
433 return Memory::Read16(addr);
434 case 4:
435 return Memory::Read32(addr);
436 case 8:
437 return Memory::Read64(addr);
438 default:
439 UNREACHABLE();
440 return 0;
441 }
442}
443
444void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
445 switch (width) {
446 case 1:
447 Memory::Write8(addr, static_cast<u8>(value));
448 break;
449 case 2:
450 Memory::Write16(addr, static_cast<u16>(value));
451 break;
452 case 4:
453 Memory::Write32(addr, static_cast<u32>(value));
454 break;
455 case 8:
456 Memory::Write64(addr, value);
457 break;
458 default:
459 UNREACHABLE();
460 }
461}
462
463CheatEngine::CheatEngine(std::vector<CheatList> cheats, const std::string& build_id,
464 VAddr code_region_start, VAddr code_region_end)
465 : cheats(std::move(cheats)) {
466 auto& core_timing{Core::System::GetInstance().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 = Core::System::GetInstance().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 auto& core_timing{Core::System::GetInstance().CoreTiming()};
482 core_timing.UnscheduleEvent(event, 0);
483}
484
485void CheatEngine::FrameCallback(u64 userdata, int cycles_late) {
486 for (auto& list : cheats)
487 list.Execute();
488
489 auto& core_timing{Core::System::GetInstance().CoreTiming()};
490 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
491}
492
493} // 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..7ed69a2c8
--- /dev/null
+++ b/src/core/file_sys/cheat_engine.h
@@ -0,0 +1,227 @@
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 <queue>
11#include "common/bit_field.h"
12#include "common/common_types.h"
13
14namespace Core::Timing {
15struct EventType;
16}
17
18namespace FileSys {
19
20enum class CodeType : u32 {
21 // 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
22 // Writes a T sized value Y to the address A added to the value of register R in memory domain M
23 WriteImmediate = 0,
24
25 // 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
26 // Compares the T sized value Y to the value at address A in memory domain M using the
27 // conditional function C. If success, continues execution. If failure, jumps to the matching
28 // EndConditional statement.
29 Conditional = 1,
30
31 // 20000000
32 // Terminates a Conditional or ConditionalInput block.
33 EndConditional = 2,
34
35 // 300R0000 VVVVVVVV
36 // Starts looping V times, storing the current count in register R.
37 // Loop block is terminated with a matching 310R0000.
38 Loop = 3,
39
40 // 400R0000 VVVVVVVV VVVVVVVV
41 // Sets the value of register R to the value V.
42 LoadImmediate = 4,
43
44 // 5TMRI0AA AAAAAAAA
45 // Sets the value of register R to the value of width T at address A in memory domain M, with
46 // the current value of R added to the address if I == 1.
47 LoadIndexed = 5,
48
49 // 6T0RIFG0 VVVVVVVV VVVVVVVV
50 // Writes the value V of width T to the memory address stored in register R. Adds the value of
51 // register G to the final calculation if F is nonzero. Increments the value of register R by T
52 // after operation if I is nonzero.
53 StoreIndexed = 6,
54
55 // 7T0RA000 VVVVVVVV
56 // Performs the arithmetic operation A on the value in register R and the value V of width T,
57 // storing the result in register R.
58 RegisterArithmetic = 7,
59
60 // 8KKKKKKK
61 // Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
62 // execution continues. If none are, execution skips to the next EndConditional command.
63 ConditionalInput = 8,
64};
65
66enum class MemoryType : u32 {
67 // Addressed relative to start of main NSO
68 MainNSO = 0,
69
70 // Addressed relative to start of heap
71 Heap = 1,
72};
73
74enum class ArithmeticOp : u32 {
75 Add = 0,
76 Sub = 1,
77 Mult = 2,
78 LShift = 3,
79 RShift = 4,
80};
81
82enum class ComparisonOp : u32 {
83 GreaterThan = 1,
84 GreaterThanEqual = 2,
85 LessThan = 3,
86 LessThanEqual = 4,
87 Equal = 5,
88 Inequal = 6,
89};
90
91union Cheat {
92 std::array<u8, 16> raw;
93
94 BitField<4, 4, CodeType> type;
95 BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
96 BitField<0, 4, u32> end_of_loop;
97 BitField<12, 4, MemoryType> memory_type;
98 BitField<8, 4, u32> register_3;
99 BitField<8, 4, ComparisonOp> comparison_op;
100 BitField<20, 4, u32> load_from_register;
101 BitField<20, 4, u32> increment_register;
102 BitField<20, 4, ArithmeticOp> arithmetic_op;
103 BitField<16, 4, u32> add_additional_register;
104 BitField<28, 4, u32> register_6;
105
106 u64 Address() const;
107 u64 ValueWidth(u64 offset) const;
108 u64 Value(u64 offset, u64 width) const;
109 u32 KeypadValue() const;
110};
111
112class CheatParser;
113
114// Represents a full collection of cheats for a game. The Execute function should be called every
115// interval that all cheats should be executed. Clients should not directly instantiate this class
116// (hence private constructor), they should instead receive an instance from CheatParser, which
117// guarantees the list is always in an acceptable state.
118class CheatList {
119public:
120 friend class CheatParser;
121
122 using Block = std::vector<Cheat>;
123 using ProgramSegment = std::vector<std::pair<std::string, Block>>;
124
125 // (width in bytes, address, value)
126 using MemoryWriter = void (*)(u32, VAddr, u64);
127 // (width in bytes, address) -> value
128 using MemoryReader = u64 (*)(u32, VAddr);
129
130 void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
131 MemoryWriter writer, MemoryReader reader);
132
133 void Execute();
134
135private:
136 CheatList(ProgramSegment master, ProgramSegment standard);
137
138 void ProcessBlockPairs(const Block& block);
139 void ExecuteSingleCheat(const Cheat& cheat);
140
141 void ExecuteBlock(const Block& block);
142
143 bool EvaluateConditional(const Cheat& cheat) const;
144
145 // Individual cheat operations
146 void WriteImmediate(const Cheat& cheat);
147 void BeginConditional(const Cheat& cheat);
148 void EndConditional(const Cheat& cheat);
149 void Loop(const Cheat& cheat);
150 void LoadImmediate(const Cheat& cheat);
151 void LoadIndexed(const Cheat& cheat);
152 void StoreIndexed(const Cheat& cheat);
153 void RegisterArithmetic(const Cheat& cheat);
154 void BeginConditionalInput(const Cheat& cheat);
155
156 VAddr SanitizeAddress(VAddr in) const;
157
158 // Master Codes are defined as codes that cannot be disabled and are run prior to all
159 // others.
160 ProgramSegment master_list;
161 // All other codes
162 ProgramSegment standard_list;
163
164 bool in_standard = false;
165
166 // 16 (0x0-0xF) scratch registers that can be used by cheats
167 std::array<u64, 16> scratch{};
168
169 MemoryWriter writer = nullptr;
170 MemoryReader reader = nullptr;
171
172 u64 main_region_begin{};
173 u64 heap_region_begin{};
174 u64 main_region_end{};
175 u64 heap_region_end{};
176
177 u64 current_block{};
178 // The current index of the cheat within the current Block
179 u64 current_index{};
180
181 // The 'stack' of the program. When a conditional or loop statement is encountered, its index is
182 // pushed onto this queue. When a end block is encountered, the condition is checked.
183 std::map<u64, u64> block_pairs;
184
185 std::set<u64> encountered_loops;
186};
187
188// Intermediary class that parses a text file or other disk format for storing cheats into a
189// CheatList object, that can be used for execution.
190class CheatParser {
191public:
192 virtual ~CheatParser();
193
194 virtual CheatList Parse(const std::vector<u8>& data) const = 0;
195
196protected:
197 CheatList MakeCheatList(CheatList::ProgramSegment master,
198 CheatList::ProgramSegment standard) const;
199};
200
201// CheatParser implementation that parses text files
202class TextCheatParser final : public CheatParser {
203public:
204 ~TextCheatParser() override;
205
206 CheatList Parse(const std::vector<u8>& data) const override;
207
208private:
209 std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
210};
211
212// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
213class CheatEngine final {
214public:
215 CheatEngine(std::vector<CheatList> cheats, const std::string& build_id, VAddr code_region_start,
216 VAddr code_region_end);
217 ~CheatEngine();
218
219private:
220 void FrameCallback(u64 userdata, int cycles_late);
221
222 Core::Timing::EventType* event;
223
224 std::vector<CheatList> cheats;
225};
226
227} // 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/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 61706966e..2b09e5d35 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"
@@ -232,6 +233,57 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
232 return !CollectPatches(patch_dirs, build_id).empty(); 233 return !CollectPatches(patch_dirs, build_id).empty();
233} 234}
234 235
236static std::optional<CheatList> ReadCheatFileFromFolder(u64 title_id,
237 const std::array<u8, 0x20>& build_id_,
238 const VirtualDir& base_path, bool upper) {
239 const auto build_id_raw = Common::HexArrayToString(build_id_, upper);
240 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
241 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
242
243 if (file == nullptr) {
244 LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
245 title_id, build_id);
246 return std::nullopt;
247 }
248
249 std::vector<u8> data(file->GetSize());
250 if (file->Read(data.data(), data.size()) != data.size()) {
251 LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
252 title_id, build_id);
253 return std::nullopt;
254 }
255
256 TextCheatParser parser;
257 return parser.Parse(data);
258}
259
260std::vector<CheatList> PatchManager::CreateCheatList(const std::array<u8, 32>& build_id_) const {
261 std::vector<CheatList> out;
262
263 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
264 auto patch_dirs = load_dir->GetSubdirectories();
265 std::sort(patch_dirs.begin(), patch_dirs.end(),
266 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
267
268 out.reserve(patch_dirs.size());
269 for (const auto& subdir : patch_dirs) {
270 auto cheats_dir = subdir->GetSubdirectory("cheats");
271 if (cheats_dir != nullptr) {
272 auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true);
273 if (res.has_value()) {
274 out.push_back(std::move(*res));
275 continue;
276 }
277
278 res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false);
279 if (res.has_value())
280 out.push_back(std::move(*res));
281 }
282 }
283
284 return out;
285}
286
235static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 287static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
236 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 288 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
237 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 289 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
@@ -403,6 +455,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
403 } 455 }
404 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) 456 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
405 AppendCommaIfNotEmpty(types, "LayeredFS"); 457 AppendCommaIfNotEmpty(types, "LayeredFS");
458 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("cheats")))
459 AppendCommaIfNotEmpty(types, "Cheats");
406 460
407 if (types.empty()) 461 if (types.empty())
408 continue; 462 continue;
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index b8a1652fd..3e3ac6aca 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -8,6 +8,7 @@
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
@@ -45,6 +46,9 @@ public:
45 // Used to prevent expensive copies in NSO loader. 46 // Used to prevent expensive copies in NSO loader.
46 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; 47 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
47 48
49 // Creates a CheatList object with all
50 std::vector<CheatList> CreateCheatList(const std::array<u8, 0x20>& build_id) const;
51
48 // Currently tracked RomFS patches: 52 // Currently tracked RomFS patches:
49 // - Game Updates 53 // - Game Updates
50 // - LayeredFS 54 // - LayeredFS
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/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/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..834fd23d2
--- /dev/null
+++ b/src/core/hle/kernel/code_set.h
@@ -0,0 +1,90 @@
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 <memory>
9#include <vector>
10
11#include "common/common_types.h"
12
13namespace Kernel {
14
15/**
16 * Represents executable data that may be loaded into a kernel process.
17 *
18 * A code set consists of three basic segments:
19 * - A code (AKA text) segment,
20 * - A read-only data segment (rodata)
21 * - A data segment
22 *
23 * The code segment is the portion of the object file that contains
24 * executable instructions.
25 *
26 * The read-only data segment in the portion of the object file that
27 * contains (as one would expect) read-only data, such as fixed constant
28 * values and data structures.
29 *
30 * The data segment is similar to the read-only data segment -- it contains
31 * variables and data structures that have predefined values, however,
32 * entities within this segment can be modified.
33 */
34struct CodeSet final {
35 /// A single segment within a code set.
36 struct Segment final {
37 /// The byte offset that this segment is located at.
38 std::size_t offset = 0;
39
40 /// The address to map this segment to.
41 VAddr addr = 0;
42
43 /// The size of this segment in bytes.
44 u32 size = 0;
45 };
46
47 explicit CodeSet();
48 ~CodeSet();
49
50 CodeSet(const CodeSet&) = delete;
51 CodeSet& operator=(const CodeSet&) = delete;
52
53 CodeSet(CodeSet&&) = default;
54 CodeSet& operator=(CodeSet&&) = default;
55
56 Segment& CodeSegment() {
57 return segments[0];
58 }
59
60 const Segment& CodeSegment() const {
61 return segments[0];
62 }
63
64 Segment& RODataSegment() {
65 return segments[1];
66 }
67
68 const Segment& RODataSegment() const {
69 return segments[1];
70 }
71
72 Segment& DataSegment() {
73 return segments[2];
74 }
75
76 const Segment& DataSegment() const {
77 return segments[2];
78 }
79
80 /// The overall data that backs this code set.
81 std::shared_ptr<std::vector<u8>> memory;
82
83 /// The segments that comprise this code set.
84 std::array<Segment, 3> segments;
85
86 /// The entry point address for this code set.
87 VAddr entrypoint = 0;
88};
89
90} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 406808ce9..0d782e4ba 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -9,6 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/file_sys/program_metadata.h" 11#include "core/file_sys/program_metadata.h"
12#include "core/hle/kernel/code_set.h"
12#include "core/hle/kernel/errors.h" 13#include "core/hle/kernel/errors.h"
13#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
@@ -31,7 +32,7 @@ namespace {
31 */ 32 */
32void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) { 33void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
33 // Setup page table so we can write to memory 34 // Setup page table so we can write to memory
34 SetCurrentPageTable(&owner_process.VMManager().page_table); 35 Memory::SetCurrentPageTable(&owner_process.VMManager().page_table);
35 36
36 // Initialize new "main" thread 37 // Initialize new "main" thread
37 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); 38 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
@@ -50,9 +51,6 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_poi
50} 51}
51} // Anonymous namespace 52} // Anonymous namespace
52 53
53CodeSet::CodeSet() = default;
54CodeSet::~CodeSet() = default;
55
56SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) { 54SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
57 auto& kernel = system.Kernel(); 55 auto& kernel = system.Kernel();
58 56
@@ -212,7 +210,7 @@ void Process::FreeTLSSlot(VAddr tls_address) {
212} 210}
213 211
214void Process::LoadModule(CodeSet module_, VAddr base_addr) { 212void Process::LoadModule(CodeSet module_, VAddr base_addr) {
215 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, 213 const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
216 MemoryState memory_state) { 214 MemoryState memory_state) {
217 const auto vma = vm_manager 215 const auto vma = vm_manager
218 .MapMemoryBlock(segment.addr + base_addr, module_.memory, 216 .MapMemoryBlock(segment.addr + base_addr, module_.memory,
@@ -222,9 +220,9 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
222 }; 220 };
223 221
224 // Map CodeSet segments 222 // Map CodeSet segments
225 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); 223 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code);
226 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); 224 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData);
227 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 225 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
228 226
229 // Clear instruction cache in CPU JIT 227 // Clear instruction cache in CPU JIT
230 system.InvalidateCpuInstructionCaches(); 228 system.InvalidateCpuInstructionCaches();
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 5e72300b6..1bd7bf5c1 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -7,7 +7,6 @@
7#include <array> 7#include <array>
8#include <bitset> 8#include <bitset>
9#include <cstddef> 9#include <cstddef>
10#include <memory>
11#include <string> 10#include <string>
12#include <vector> 11#include <vector>
13#include <boost/container/static_vector.hpp> 12#include <boost/container/static_vector.hpp>
@@ -34,6 +33,8 @@ class KernelCore;
34class ResourceLimit; 33class ResourceLimit;
35class Thread; 34class Thread;
36 35
36struct CodeSet;
37
37struct AddressMapping { 38struct AddressMapping {
38 // Address and size must be page-aligned 39 // Address and size must be page-aligned
39 VAddr address; 40 VAddr address;
@@ -66,46 +67,6 @@ enum class ProcessStatus {
66 DebugBreak, 67 DebugBreak,
67}; 68};
68 69
69struct CodeSet final {
70 struct Segment {
71 std::size_t offset = 0;
72 VAddr addr = 0;
73 u32 size = 0;
74 };
75
76 explicit CodeSet();
77 ~CodeSet();
78
79 Segment& CodeSegment() {
80 return segments[0];
81 }
82
83 const Segment& CodeSegment() const {
84 return segments[0];
85 }
86
87 Segment& RODataSegment() {
88 return segments[1];
89 }
90
91 const Segment& RODataSegment() const {
92 return segments[1];
93 }
94
95 Segment& DataSegment() {
96 return segments[2];
97 }
98
99 const Segment& DataSegment() const {
100 return segments[2];
101 }
102
103 std::shared_ptr<std::vector<u8>> memory;
104
105 std::array<Segment, 3> segments;
106 VAddr entrypoint = 0;
107};
108
109class Process final : public WaitObject { 70class Process final : public WaitObject {
110public: 71public:
111 enum : u64 { 72 enum : u64 {
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 5fccfd9f4..cc189cc64 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -96,7 +96,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
96 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 96 auto* const thread_owner_process = current_thread->GetOwnerProcess();
97 if (previous_process != thread_owner_process) { 97 if (previous_process != thread_owner_process) {
98 system.Kernel().MakeCurrentProcess(thread_owner_process); 98 system.Kernel().MakeCurrentProcess(thread_owner_process);
99 SetCurrentPageTable(&thread_owner_process->VMManager().page_table); 99 Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
100 } 100 }
101 101
102 cpu_core.LoadContext(new_thread->GetContext()); 102 cpu_core.LoadContext(new_thread->GetContext());
@@ -199,8 +199,7 @@ void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
199 ASSERT(thread->GetPriority() < THREADPRIO_COUNT); 199 ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
200 200
201 // Yield this thread -- sleep for zero time and force reschedule to different thread 201 // Yield this thread -- sleep for zero time and force reschedule to different thread
202 WaitCurrentThread_Sleep(); 202 GetCurrentThread()->Sleep(0);
203 GetCurrentThread()->WakeAfterDelay(0);
204} 203}
205 204
206void Scheduler::YieldWithLoadBalancing(Thread* thread) { 205void Scheduler::YieldWithLoadBalancing(Thread* thread) {
@@ -215,8 +214,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
215 ASSERT(priority < THREADPRIO_COUNT); 214 ASSERT(priority < THREADPRIO_COUNT);
216 215
217 // Sleep for zero time to be able to force reschedule to different thread 216 // Sleep for zero time to be able to force reschedule to different thread
218 WaitCurrentThread_Sleep(); 217 GetCurrentThread()->Sleep(0);
219 GetCurrentThread()->WakeAfterDelay(0);
220 218
221 Thread* suggested_thread = nullptr; 219 Thread* suggested_thread = nullptr;
222 220
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 599609944..a6a17efe7 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1285,10 +1285,14 @@ static ResultCode StartThread(Handle thread_handle) {
1285 1285
1286/// Called when a thread exits 1286/// Called when a thread exits
1287static void ExitThread() { 1287static void ExitThread() {
1288 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC()); 1288 auto& system = Core::System::GetInstance();
1289 1289
1290 ExitCurrentThread(); 1290 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
1291 Core::System::GetInstance().PrepareReschedule(); 1291
1292 auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
1293 current_thread->Stop();
1294 system.CurrentScheduler().RemoveThread(current_thread);
1295 system.PrepareReschedule();
1292} 1296}
1293 1297
1294/// Sleep the current thread 1298/// Sleep the current thread
@@ -1301,32 +1305,32 @@ static void SleepThread(s64 nanoseconds) {
1301 YieldAndWaitForLoadBalancing = -2, 1305 YieldAndWaitForLoadBalancing = -2,
1302 }; 1306 };
1303 1307
1308 auto& system = Core::System::GetInstance();
1309 auto& scheduler = system.CurrentScheduler();
1310 auto* const current_thread = scheduler.GetCurrentThread();
1311
1304 if (nanoseconds <= 0) { 1312 if (nanoseconds <= 0) {
1305 auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
1306 switch (static_cast<SleepType>(nanoseconds)) { 1313 switch (static_cast<SleepType>(nanoseconds)) {
1307 case SleepType::YieldWithoutLoadBalancing: 1314 case SleepType::YieldWithoutLoadBalancing:
1308 scheduler.YieldWithoutLoadBalancing(GetCurrentThread()); 1315 scheduler.YieldWithoutLoadBalancing(current_thread);
1309 break; 1316 break;
1310 case SleepType::YieldWithLoadBalancing: 1317 case SleepType::YieldWithLoadBalancing:
1311 scheduler.YieldWithLoadBalancing(GetCurrentThread()); 1318 scheduler.YieldWithLoadBalancing(current_thread);
1312 break; 1319 break;
1313 case SleepType::YieldAndWaitForLoadBalancing: 1320 case SleepType::YieldAndWaitForLoadBalancing:
1314 scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread()); 1321 scheduler.YieldAndWaitForLoadBalancing(current_thread);
1315 break; 1322 break;
1316 default: 1323 default:
1317 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); 1324 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
1318 } 1325 }
1319 } else { 1326 } else {
1320 // Sleep current thread and check for next thread to schedule 1327 current_thread->Sleep(nanoseconds);
1321 WaitCurrentThread_Sleep();
1322
1323 // Create an event to wake the thread up after the specified nanosecond delay has passed
1324 GetCurrentThread()->WakeAfterDelay(nanoseconds);
1325 } 1328 }
1326 1329
1327 // Reschedule all CPU cores 1330 // Reschedule all CPU cores
1328 for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) 1331 for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
1329 Core::System::GetInstance().CpuCore(i).PrepareReschedule(); 1332 system.CpuCore(i).PrepareReschedule();
1333 }
1330} 1334}
1331 1335
1332/// Wait process wide key atomic 1336/// Wait process wide key atomic
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index eb54d6651..3b22e8e0d 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"
@@ -68,17 +66,6 @@ void Thread::Stop() {
68 owner_process->FreeTLSSlot(tls_address); 66 owner_process->FreeTLSSlot(tls_address);
69} 67}
70 68
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) { 69void Thread::WakeAfterDelay(s64 nanoseconds) {
83 // Don't schedule a wakeup if the thread wants to wait forever 70 // Don't schedule a wakeup if the thread wants to wait forever
84 if (nanoseconds == -1) 71 if (nanoseconds == -1)
@@ -269,8 +256,8 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
269 if (thread->lock_owner == this) { 256 if (thread->lock_owner == this) {
270 // If the thread is already waiting for this thread to release the mutex, ensure that the 257 // 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. 258 // waiters list is consistent and return without doing anything.
272 auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); 259 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
273 ASSERT(itr != wait_mutex_threads.end()); 260 ASSERT(iter != wait_mutex_threads.end());
274 return; 261 return;
275 } 262 }
276 263
@@ -278,11 +265,16 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
278 ASSERT(thread->lock_owner == nullptr); 265 ASSERT(thread->lock_owner == nullptr);
279 266
280 // Ensure that the thread is not already in the list of mutex waiters 267 // 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); 268 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
282 ASSERT(itr == wait_mutex_threads.end()); 269 ASSERT(iter == wait_mutex_threads.end());
283 270
271 // Keep the list in an ordered fashion
272 const auto insertion_point = std::find_if(
273 wait_mutex_threads.begin(), wait_mutex_threads.end(),
274 [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
275 wait_mutex_threads.insert(insertion_point, thread);
284 thread->lock_owner = this; 276 thread->lock_owner = this;
285 wait_mutex_threads.emplace_back(std::move(thread)); 277
286 UpdatePriority(); 278 UpdatePriority();
287} 279}
288 280
@@ -290,32 +282,44 @@ void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
290 ASSERT(thread->lock_owner == this); 282 ASSERT(thread->lock_owner == this);
291 283
292 // Ensure that the thread is in the list of mutex waiters 284 // 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); 285 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
294 ASSERT(itr != wait_mutex_threads.end()); 286 ASSERT(iter != wait_mutex_threads.end());
287
288 wait_mutex_threads.erase(iter);
295 289
296 boost::remove_erase(wait_mutex_threads, thread);
297 thread->lock_owner = nullptr; 290 thread->lock_owner = nullptr;
298 UpdatePriority(); 291 UpdatePriority();
299} 292}
300 293
301void Thread::UpdatePriority() { 294void Thread::UpdatePriority() {
302 // Find the highest priority among all the threads that are waiting for this thread's lock 295 // If any of the threads waiting on the mutex have a higher priority
296 // (taking into account priority inheritance), then this thread inherits
297 // that thread's priority.
303 u32 new_priority = nominal_priority; 298 u32 new_priority = nominal_priority;
304 for (const auto& thread : wait_mutex_threads) { 299 if (!wait_mutex_threads.empty()) {
305 if (thread->nominal_priority < new_priority) 300 if (wait_mutex_threads.front()->current_priority < new_priority) {
306 new_priority = thread->nominal_priority; 301 new_priority = wait_mutex_threads.front()->current_priority;
302 }
307 } 303 }
308 304
309 if (new_priority == current_priority) 305 if (new_priority == current_priority) {
310 return; 306 return;
307 }
311 308
312 scheduler->SetThreadPriority(this, new_priority); 309 scheduler->SetThreadPriority(this, new_priority);
313
314 current_priority = new_priority; 310 current_priority = new_priority;
315 311
312 if (!lock_owner) {
313 return;
314 }
315
316 // Ensure that the thread is within the correct location in the waiting list.
317 auto old_owner = lock_owner;
318 lock_owner->RemoveMutexWaiter(this);
319 old_owner->AddMutexWaiter(this);
320
316 // Recursively update the priority of the thread that depends on the priority of this one. 321 // Recursively update the priority of the thread that depends on the priority of this one.
317 if (lock_owner) 322 lock_owner->UpdatePriority();
318 lock_owner->UpdatePriority();
319} 323}
320 324
321void Thread::ChangeCore(u32 core, u64 mask) { 325void Thread::ChangeCore(u32 core, u64 mask) {
@@ -391,6 +395,14 @@ void Thread::SetActivity(ThreadActivity value) {
391 } 395 }
392} 396}
393 397
398void Thread::Sleep(s64 nanoseconds) {
399 // Sleep current thread and check for next thread to schedule
400 SetStatus(ThreadStatus::WaitSleep);
401
402 // Create an event to wake the thread up after the specified nanosecond delay has passed
403 WakeAfterDelay(nanoseconds);
404}
405
394//////////////////////////////////////////////////////////////////////////////////////////////////// 406////////////////////////////////////////////////////////////////////////////////////////////////////
395 407
396/** 408/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index c48b21aba..faad5f391 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -383,6 +383,9 @@ public:
383 383
384 void SetActivity(ThreadActivity value); 384 void SetActivity(ThreadActivity value);
385 385
386 /// Sleeps this thread for the given amount of nanoseconds.
387 void Sleep(s64 nanoseconds);
388
386private: 389private:
387 explicit Thread(KernelCore& kernel); 390 explicit Thread(KernelCore& kernel);
388 ~Thread() override; 391 ~Thread() override;
@@ -398,8 +401,14 @@ private:
398 VAddr entry_point = 0; 401 VAddr entry_point = 0;
399 VAddr stack_top = 0; 402 VAddr stack_top = 0;
400 403
401 u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application 404 /// Nominal thread priority, as set by the emulated application.
402 u32 current_priority = 0; ///< Current thread priority, can be temporarily changed 405 /// The nominal priority is the thread priority without priority
406 /// inheritance taken into account.
407 u32 nominal_priority = 0;
408
409 /// Current thread priority. This may change over the course of the
410 /// thread's lifetime in order to facilitate priority inheritance.
411 u32 current_priority = 0;
403 412
404 u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. 413 u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
405 u64 last_running_ticks = 0; ///< CPU tick when thread was last running 414 u64 last_running_ticks = 0; ///< CPU tick when thread was last running
@@ -460,14 +469,4 @@ private:
460 */ 469 */
461Thread* GetCurrentThread(); 470Thread* GetCurrentThread();
462 471
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 472} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 05c59af34..22bf55ce7 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;
@@ -624,7 +624,7 @@ void VMManager::ClearPageTable() {
624 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); 624 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
625 page_table.special_regions.clear(); 625 page_table.special_regions.clear();
626 std::fill(page_table.attributes.begin(), page_table.attributes.end(), 626 std::fill(page_table.attributes.begin(), page_table.attributes.end(),
627 Memory::PageType::Unmapped); 627 Common::PageType::Unmapped);
628} 628}
629 629
630VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask, 630VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 88e0b3c02..7cdff6094 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);
@@ -509,7 +510,7 @@ public:
509 510
510 /// Each VMManager has its own page table, which is set as the main one when the owning process 511 /// Each VMManager has its own page table, which is set as the main one when the owning process
511 /// is scheduled. 512 /// is scheduled.
512 Memory::PageTable page_table; 513 Common::PageTable page_table{Memory::PAGE_BITS};
513 514
514private: 515private:
515 using VMAIter = VMAMap::iterator; 516 using VMAIter = VMAMap::iterator;
@@ -616,6 +617,9 @@ private:
616 VAddr new_map_region_base = 0; 617 VAddr new_map_region_base = 0;
617 VAddr new_map_region_end = 0; 618 VAddr new_map_region_end = 0;
618 619
620 VAddr main_code_region_base = 0;
621 VAddr main_code_region_end = 0;
622
619 VAddr tls_io_region_base = 0; 623 VAddr tls_io_region_base = 0;
620 VAddr tls_io_region_end = 0; 624 VAddr tls_io_region_end = 0;
621 625
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 3f009d2b7..9c44e27c6 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"
@@ -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)
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b6113cfdd..565dd8e9e 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> {
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/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 54959edd8..f03fb629c 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -733,7 +733,10 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
733FSP_SRV::~FSP_SRV() = default; 733FSP_SRV::~FSP_SRV() = default;
734 734
735void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) { 735void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
736 LOG_WARNING(Service_FS, "(STUBBED) called"); 736 IPC::RequestParser rp{ctx};
737 current_process_id = rp.Pop<u64>();
738
739 LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
737 740
738 IPC::ResponseBuilder rb{ctx, 2}; 741 IPC::ResponseBuilder rb{ctx, 2};
739 rb.Push(RESULT_SUCCESS); 742 rb.Push(RESULT_SUCCESS);
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.h b/src/core/hle/service/hid/hid.h
index 7cc58db4c..498602de5 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
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/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/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index a34b9e753..b031ebc66 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"
@@ -178,7 +179,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
178 auto& gpu = system_instance.GPU(); 179 auto& gpu = system_instance.GPU();
179 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset); 180 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
180 ASSERT(cpu_addr); 181 ASSERT(cpu_addr);
181 gpu.FlushAndInvalidateRegion(*cpu_addr, itr->second.size); 182 gpu.FlushAndInvalidateRegion(ToCacheAddr(Memory::GetPointer(*cpu_addr)), itr->second.size);
182 183
183 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); 184 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
184 185
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 6057c7f26..8b1920f22 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"
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..5de02a94b 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"
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..0eb9fd7f7 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -7,10 +7,13 @@
7#include <lz4.h> 7#include <lz4.h>
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"
11#include "common/swap.h" 12#include "common/swap.h"
13#include "core/core.h"
12#include "core/file_sys/patch_manager.h" 14#include "core/file_sys/patch_manager.h"
13#include "core/gdbstub/gdbstub.h" 15#include "core/gdbstub/gdbstub.h"
16#include "core/hle/kernel/code_set.h"
14#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/vm_manager.h" 18#include "core/hle/kernel/vm_manager.h"
16#include "core/loader/nso.h" 19#include "core/loader/nso.h"
@@ -164,6 +167,16 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
164 std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size()); 167 std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size());
165 } 168 }
166 169
170 // Apply cheats if they exist and the program has a valid title ID
171 if (pm) {
172 const auto cheats = pm->CreateCheatList(nso_header.build_id);
173 if (!cheats.empty()) {
174 Core::System::GetInstance().RegisterCheatList(
175 cheats, Common::HexArrayToString(nso_header.build_id), load_base,
176 load_base + program_image.size());
177 }
178 }
179
167 // Load codeset for current process 180 // Load codeset for current process
168 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 181 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
169 process.LoadModule(std::move(codeset), load_base); 182 process.LoadModule(std::move(codeset), load_base);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 135b6ea5a..167c8a694 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -6,8 +6,8 @@
6 6
7#include <optional> 7#include <optional>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/swap.h"
9#include "core/file_sys/patch_manager.h" 10#include "core/file_sys/patch_manager.h"
10#include "core/loader/linker.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
13namespace Kernel { 13namespace Kernel {
@@ -26,7 +26,7 @@ struct NSOArgumentHeader {
26static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size."); 26static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
27 27
28/// Loads an NSO file 28/// Loads an NSO file
29class AppLoader_NSO final : public AppLoader, Linker { 29class AppLoader_NSO final : public AppLoader {
30public: 30public:
31 explicit AppLoader_NSO(FileSys::VirtualFile file); 31 explicit AppLoader_NSO(FileSys::VirtualFile file);
32 32
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 6591c45d2..365ac82b4 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 (current_page_table) {
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/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..b132d77f5 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -475,12 +475,11 @@ SDLState::SDLState() {
475 475
476 initialized = true; 476 initialized = true;
477 if (start_thread) { 477 if (start_thread) {
478 poll_thread = std::thread([&] { 478 poll_thread = std::thread([this] {
479 using namespace std::chrono_literals; 479 using namespace std::chrono_literals;
480 SDL_Event event;
481 while (initialized) { 480 while (initialized) {
482 SDL_PumpEvents(); 481 SDL_PumpEvents();
483 std::this_thread::sleep_for(std::chrono::duration(10ms)); 482 std::this_thread::sleep_for(10ms);
484 } 483 }
485 }); 484 });
486 } 485 }
@@ -651,9 +650,9 @@ private:
651}; 650};
652} // namespace Polling 651} // namespace Polling
653 652
654std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPollers( 653SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
655 InputCommon::Polling::DeviceType type) { 654 Pollers pollers;
656 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers; 655
657 switch (type) { 656 switch (type) {
658 case InputCommon::Polling::DeviceType::Analog: 657 case InputCommon::Polling::DeviceType::Analog:
659 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); 658 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
@@ -661,8 +660,9 @@ std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPo
661 case InputCommon::Polling::DeviceType::Button: 660 case InputCommon::Polling::DeviceType::Button:
662 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 661 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
663 break; 662 break;
664 return pollers;
665 } 663 }
664
665 return pollers;
666} 666}
667 667
668} // namespace SDL 668} // 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..d0284bdf4 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,4 +1,5 @@
1add_executable(tests 1add_executable(tests
2 common/bit_field.cpp
2 common/param_package.cpp 3 common/param_package.cpp
3 common/ring_buffer.cpp 4 common/ring_buffer.cpp
4 core/arm/arm_test_common.cpp 5 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/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 0c3038c52..14b76680f 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -123,6 +123,8 @@ if (ENABLE_VULKAN)
123 renderer_vulkan/vk_memory_manager.h 123 renderer_vulkan/vk_memory_manager.h
124 renderer_vulkan/vk_resource_manager.cpp 124 renderer_vulkan/vk_resource_manager.cpp
125 renderer_vulkan/vk_resource_manager.h 125 renderer_vulkan/vk_resource_manager.h
126 renderer_vulkan/vk_sampler_cache.cpp
127 renderer_vulkan/vk_sampler_cache.h
126 renderer_vulkan/vk_scheduler.cpp 128 renderer_vulkan/vk_scheduler.cpp
127 renderer_vulkan/vk_scheduler.h 129 renderer_vulkan/vk_scheduler.h
128 renderer_vulkan/vk_stream_buffer.cpp 130 renderer_vulkan/vk_stream_buffer.cpp
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/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index aae2a4019..0931b9626 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -9,6 +9,7 @@
9#include "video_core/engines/kepler_memory.h" 9#include "video_core/engines/kepler_memory.h"
10#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.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
@@ -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.Write32(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/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 144e7fa82..c5d5be4ef 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -270,11 +270,9 @@ void Maxwell3D::ProcessMacroBind(u32 data) {
270} 270}
271 271
272void Maxwell3D::ProcessQueryGet() { 272void Maxwell3D::ProcessQueryGet() {
273 GPUVAddr sequence_address = regs.query.QueryAddress(); 273 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 274 // Since the sequence address is given as a GPU VAddr, we have to convert it to an application
275 // VAddr before writing. 275 // VAddr before writing.
276 const auto address = memory_manager.GpuToCpuAddress(sequence_address);
277 ASSERT_MSG(address, "Invalid GPU address");
278 276
279 // TODO(Subv): Support the other query units. 277 // TODO(Subv): Support the other query units.
280 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, 278 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
@@ -309,7 +307,7 @@ void Maxwell3D::ProcessQueryGet() {
309 // Write the current query sequence to the sequence address. 307 // 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 308 // TODO(Subv): Find out what happens if you use a long query type but mark it as a short
311 // query. 309 // query.
312 Memory::Write32(*address, sequence); 310 memory_manager.Write32(sequence_address, sequence);
313 } else { 311 } else {
314 // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast 312 // 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 313 // GPU, this command may actually take a while to complete in real hardware due to GPU
@@ -318,7 +316,7 @@ void Maxwell3D::ProcessQueryGet() {
318 query_result.value = result; 316 query_result.value = result;
319 // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming 317 // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
320 query_result.timestamp = system.CoreTiming().GetTicks(); 318 query_result.timestamp = system.CoreTiming().GetTicks();
321 Memory::WriteBlock(*address, &query_result, sizeof(query_result)); 319 memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result));
322 } 320 }
323 dirty_flags.OnMemoryWrite(); 321 dirty_flags.OnMemoryWrite();
324 break; 322 break;
@@ -393,10 +391,12 @@ void Maxwell3D::ProcessCBData(u32 value) {
393 // Don't allow writing past the end of the buffer. 391 // Don't allow writing past the end of the buffer.
394 ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size); 392 ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
395 393
396 const auto address = memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos); 394 const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos};
397 ASSERT_MSG(address, "Invalid GPU address"); 395
396 u8* ptr{memory_manager.GetPointer(address)};
397 rasterizer.InvalidateRegion(ToCacheAddr(ptr), sizeof(u32));
398 memory_manager.Write32(address, value);
398 399
399 Memory::Write32(*address, value);
400 dirty_flags.OnMemoryWrite(); 400 dirty_flags.OnMemoryWrite();
401 401
402 // Increment the current buffer position. 402 // Increment the current buffer position.
@@ -404,14 +404,10 @@ void Maxwell3D::ProcessCBData(u32 value) {
404} 404}
405 405
406Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { 406Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
407 const GPUVAddr tic_base_address = regs.tic.TICAddress(); 407 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 408
413 Texture::TICEntry tic_entry; 409 Texture::TICEntry tic_entry;
414 Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry)); 410 memory_manager.ReadBlock(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
415 411
416 ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear || 412 ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear ||
417 tic_entry.header_version == Texture::TICHeaderVersion::Pitch, 413 tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
@@ -429,14 +425,10 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
429} 425}
430 426
431Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { 427Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
432 const GPUVAddr tsc_base_address = regs.tsc.TSCAddress(); 428 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 429
438 Texture::TSCEntry tsc_entry; 430 Texture::TSCEntry tsc_entry;
439 Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry)); 431 memory_manager.ReadBlock(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
440 return tsc_entry; 432 return tsc_entry;
441} 433}
442 434
@@ -455,10 +447,7 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
455 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; 447 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset;
456 current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { 448 current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) {
457 449
458 const auto address = memory_manager.GpuToCpuAddress(current_texture); 450 const Texture::TextureHandle tex_handle{memory_manager.Read32(current_texture)};
459 ASSERT_MSG(address, "Invalid GPU address");
460
461 const Texture::TextureHandle tex_handle{Memory::Read32(*address)};
462 451
463 Texture::FullTextureInfo tex_info{}; 452 Texture::FullTextureInfo tex_info{};
464 // TODO(Subv): Use the shader to determine which textures are actually accessed. 453 // TODO(Subv): Use the shader to determine which textures are actually accessed.
@@ -493,10 +482,7 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
493 482
494 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); 483 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
495 484
496 const auto tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address); 485 const Texture::TextureHandle tex_handle{memory_manager.Read32(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 486
501 Texture::FullTextureInfo tex_info{}; 487 Texture::FullTextureInfo tex_info{};
502 tex_info.index = static_cast<u32>(offset); 488 tex_info.index = static_cast<u32>(offset);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 9dfea5999..a0ded4c25 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -9,6 +9,7 @@
9#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/engines/maxwell_dma.h" 10#include "video_core/engines/maxwell_dma.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 {
@@ -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,18 @@ 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
92 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { 91 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
93 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated 92 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
94 // copying. 93 // copying.
95 Core::System::GetInstance().GPU().FlushRegion(*source_cpu, src_size); 94 rasterizer.FlushRegion(ToCacheAddr(source_ptr), src_size);
96 95
97 // We have to invalidate the destination region to evict any outdated surfaces from the 96 // 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 97 // 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. 98 // might contain a dirty surface that will have to be written back to memory.
100 Core::System::GetInstance().GPU().InvalidateRegion(*dest_cpu, dst_size); 99 rasterizer.InvalidateRegion(ToCacheAddr(dst_ptr), dst_size);
101 }; 100 };
102 101
103 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 102 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
@@ -110,8 +109,8 @@ void MaxwellDMA::HandleCopy() {
110 copy_size * src_bytes_per_pixel); 109 copy_size * src_bytes_per_pixel);
111 110
112 Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch, 111 Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
113 regs.src_params.size_x, src_bytes_per_pixel, *source_cpu, 112 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, 113 regs.src_params.BlockHeight(), regs.src_params.pos_x,
115 regs.src_params.pos_y); 114 regs.src_params.pos_y);
116 } else { 115 } else {
117 ASSERT(regs.dst_params.size_z == 1); 116 ASSERT(regs.dst_params.size_z == 1);
@@ -124,7 +123,7 @@ void MaxwellDMA::HandleCopy() {
124 123
125 // If the input is linear and the output is tiled, swizzle the input and copy it over. 124 // 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, 125 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()); 126 src_bpp, dst_ptr, source_ptr, regs.dst_params.BlockHeight());
128 } 127 }
129} 128}
130 129
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 08abf8ac9..66c690494 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -274,7 +274,6 @@ void GPU::ProcessSemaphoreTriggerMethod() {
274 const auto op = 274 const auto op =
275 static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask); 275 static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
276 if (op == GpuSemaphoreOperation::WriteLong) { 276 if (op == GpuSemaphoreOperation::WriteLong) {
277 auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
278 struct Block { 277 struct Block {
279 u32 sequence; 278 u32 sequence;
280 u32 zeros = 0; 279 u32 zeros = 0;
@@ -286,11 +285,9 @@ void GPU::ProcessSemaphoreTriggerMethod() {
286 // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of 285 // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
287 // CoreTiming 286 // CoreTiming
288 block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks(); 287 block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
289 Memory::WriteBlock(*address, &block, sizeof(block)); 288 memory_manager->WriteBlock(regs.smaphore_address.SmaphoreAddress(), &block, sizeof(block));
290 } else { 289 } else {
291 const auto address = 290 const u32 word{memory_manager->Read32(regs.smaphore_address.SmaphoreAddress())};
292 memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
293 const u32 word = Memory::Read32(*address);
294 if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || 291 if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) ||
295 (op == GpuSemaphoreOperation::AcquireGequal && 292 (op == GpuSemaphoreOperation::AcquireGequal &&
296 static_cast<s32>(word - regs.semaphore_sequence) > 0) || 293 static_cast<s32>(word - regs.semaphore_sequence) > 0) ||
@@ -317,13 +314,11 @@ void GPU::ProcessSemaphoreTriggerMethod() {
317} 314}
318 315
319void GPU::ProcessSemaphoreRelease() { 316void GPU::ProcessSemaphoreRelease() {
320 const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress()); 317 memory_manager->Write32(regs.smaphore_address.SmaphoreAddress(), regs.semaphore_release);
321 Memory::Write32(*address, regs.semaphore_release);
322} 318}
323 319
324void GPU::ProcessSemaphoreAcquire() { 320void GPU::ProcessSemaphoreAcquire() {
325 const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress()); 321 const u32 word = memory_manager->Read32(regs.smaphore_address.SmaphoreAddress());
326 const u32 word = Memory::Read32(*address);
327 const auto value = regs.semaphore_acquire; 322 const auto value = regs.semaphore_acquire;
328 if (word != value) { 323 if (word != value) {
329 regs.acquire_active = true; 324 regs.acquire_active = true;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 56a203275..a14b95c30 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -11,6 +11,11 @@
11#include "video_core/dma_pusher.h" 11#include "video_core/dma_pusher.h"
12#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
13 13
14using CacheAddr = std::uintptr_t;
15inline CacheAddr ToCacheAddr(const void* host_ptr) {
16 return reinterpret_cast<CacheAddr>(host_ptr);
17}
18
14namespace Core { 19namespace Core {
15class System; 20class System;
16} 21}
@@ -209,13 +214,13 @@ public:
209 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0; 214 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0;
210 215
211 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 216 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
212 virtual void FlushRegion(VAddr addr, u64 size) = 0; 217 virtual void FlushRegion(CacheAddr addr, u64 size) = 0;
213 218
214 /// Notify rasterizer that any caches of the specified region should be invalidated 219 /// Notify rasterizer that any caches of the specified region should be invalidated
215 virtual void InvalidateRegion(VAddr addr, u64 size) = 0; 220 virtual void InvalidateRegion(CacheAddr addr, u64 size) = 0;
216 221
217 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated 222 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
218 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 223 virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0;
219 224
220private: 225private:
221 void ProcessBindMethod(const MethodCall& method_call); 226 void ProcessBindMethod(const MethodCall& method_call);
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index ad0a747e3..8b355cf7b 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -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..086b2f625 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -5,7 +5,6 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "core/frontend/scope_acquire_window_context.h" 7#include "core/frontend/scope_acquire_window_context.h"
8#include "core/settings.h"
9#include "video_core/dma_pusher.h" 8#include "video_core/dma_pusher.h"
10#include "video_core/gpu.h" 9#include "video_core/gpu.h"
11#include "video_core/gpu_thread.h" 10#include "video_core/gpu_thread.h"
@@ -13,38 +12,13 @@
13 12
14namespace VideoCommon::GPUThread { 13namespace VideoCommon::GPUThread {
15 14
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 15/// Runs the GPU thread
36static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher, 16static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher,
37 SynchState& state) { 17 SynchState& state) {
38
39 MicroProfileOnThreadCreate("GpuThread"); 18 MicroProfileOnThreadCreate("GpuThread");
40 19
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 20 // Wait for first GPU command before acquiring the window context
47 WaitForWakeup(); 21 state.WaitForCommands();
48 22
49 // If emulation was stopped during disk shader loading, abort before trying to acquire context 23 // If emulation was stopped during disk shader loading, abort before trying to acquire context
50 if (!state.is_running) { 24 if (!state.is_running) {
@@ -53,100 +27,72 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
53 27
54 Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()}; 28 Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()};
55 29
30 CommandDataContainer next;
56 while (state.is_running) { 31 while (state.is_running) {
57 if (!state.is_running) { 32 state.WaitForCommands();
58 return; 33 while (!state.queue.Empty()) {
59 } 34 state.queue.Pop(next);
60 35 if (const auto submit_list = std::get_if<SubmitListCommand>(&next.data)) {
61 { 36 dma_pusher.Push(std::move(submit_list->entries));
62 // Thread has been woken up, so make the previous write queue the next read queue 37 dma_pusher.DispatchCalls();
63 std::lock_guard<std::mutex> lock{state.signal_mutex}; 38 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
64 std::swap(state.push_queue, state.pop_queue); 39 state.DecrementFramesCounter();
65 } 40 renderer.SwapBuffers(std::move(data->framebuffer));
66 41 } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) {
67 // Execute all of the GPU commands 42 renderer.Rasterizer().FlushRegion(data->addr, data->size);
68 while (!state.pop_queue->empty()) { 43 } else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) {
69 ExecuteCommand(&state.pop_queue->front(), renderer, dma_pusher); 44 renderer.Rasterizer().InvalidateRegion(data->addr, data->size);
70 state.pop_queue->pop(); 45 } else if (const auto data = std::get_if<EndProcessingCommand>(&next.data)) {
46 return;
47 } else {
48 UNREACHABLE();
49 }
71 } 50 }
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 } 51 }
83} 52}
84 53
85ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) 54ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher)
86 : renderer{renderer}, dma_pusher{dma_pusher}, thread{RunThread, std::ref(renderer), 55 : renderer{renderer}, dma_pusher{dma_pusher}, thread{RunThread, std::ref(renderer),
87 std::ref(dma_pusher), std::ref(state)}, 56 std::ref(dma_pusher), std::ref(state)} {}
88 thread_id{thread.get_id()} {}
89 57
90ThreadManager::~ThreadManager() { 58ThreadManager::~ThreadManager() {
91 { 59 // Notify GPU thread that a shutdown is pending
92 // Notify GPU thread that a shutdown is pending 60 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(); 61 thread.join();
99} 62}
100 63
101void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 64void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
102 if (entries.empty()) { 65 PushCommand(SubmitListCommand(std::move(entries)));
103 return;
104 }
105
106 PushCommand(SubmitListCommand(std::move(entries)), false, false);
107} 66}
108 67
109void ThreadManager::SwapBuffers( 68void ThreadManager::SwapBuffers(
110 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { 69 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
111 PushCommand(SwapBuffersCommand(std::move(framebuffer)), true, false); 70 state.IncrementFramesCounter();
71 PushCommand(SwapBuffersCommand(std::move(framebuffer)));
72 state.WaitForFrames();
112} 73}
113 74
114void ThreadManager::FlushRegion(VAddr addr, u64 size) { 75void ThreadManager::FlushRegion(CacheAddr addr, u64 size) {
115 // Block the CPU when using accurate emulation 76 PushCommand(FlushRegionCommand(addr, size));
116 PushCommand(FlushRegionCommand(addr, size), Settings::values.use_accurate_gpu_emulation, false);
117} 77}
118 78
119void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { 79void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) {
120 PushCommand(InvalidateRegionCommand(addr, size), true, true); 80 if (state.queue.Empty()) {
81 // It's quicker to invalidate a single region on the CPU if the queue is already empty
82 renderer.Rasterizer().InvalidateRegion(addr, size);
83 } else {
84 PushCommand(InvalidateRegionCommand(addr, size));
85 }
121} 86}
122 87
123void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { 88void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
89 // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important
124 InvalidateRegion(addr, size); 90 InvalidateRegion(addr, size);
125} 91}
126 92
127void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu) { 93void ThreadManager::PushCommand(CommandData&& command_data) {
128 { 94 state.queue.Push(CommandDataContainer(std::move(command_data)));
129 std::lock_guard<std::mutex> lock{state.signal_mutex}; 95 state.SignalCommands();
130
131 if ((allow_on_cpu && state.is_idle) || IsGpuThread()) {
132 // Execute the command synchronously on the current thread
133 ExecuteCommand(&command_data, renderer, dma_pusher);
134 return;
135 }
136
137 // Push the command to the GPU thread
138 state.UpdateIdleState();
139 state.push_queue->emplace(command_data);
140 }
141
142 // Signal the GPU thread that commands are pending
143 state.signal_condition.notify_one();
144
145 if (wait_for_idle) {
146 // Wait for the GPU to be idle (all commands to be executed)
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 }
150} 96}
151 97
152} // namespace VideoCommon::GPUThread 98} // namespace VideoCommon::GPUThread
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index edb148b14..8cd7db1c6 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -13,6 +13,9 @@
13#include <thread> 13#include <thread>
14#include <variant> 14#include <variant>
15 15
16#include "common/threadsafe_queue.h"
17#include "video_core/gpu.h"
18
16namespace Tegra { 19namespace Tegra {
17struct FramebufferConfig; 20struct FramebufferConfig;
18class DmaPusher; 21class DmaPusher;
@@ -24,6 +27,9 @@ class RendererBase;
24 27
25namespace VideoCommon::GPUThread { 28namespace VideoCommon::GPUThread {
26 29
30/// Command to signal to the GPU thread that processing has ended
31struct EndProcessingCommand final {};
32
27/// Command to signal to the GPU thread that a command list is ready for processing 33/// Command to signal to the GPU thread that a command list is ready for processing
28struct SubmitListCommand final { 34struct SubmitListCommand final {
29 explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {} 35 explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {}
@@ -36,59 +42,110 @@ struct SwapBuffersCommand final {
36 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer) 42 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer)
37 : framebuffer{std::move(framebuffer)} {} 43 : framebuffer{std::move(framebuffer)} {}
38 44
39 std::optional<const Tegra::FramebufferConfig> framebuffer; 45 std::optional<Tegra::FramebufferConfig> framebuffer;
40}; 46};
41 47
42/// Command to signal to the GPU thread to flush a region 48/// Command to signal to the GPU thread to flush a region
43struct FlushRegionCommand final { 49struct FlushRegionCommand final {
44 explicit constexpr FlushRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {} 50 explicit constexpr FlushRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {}
45 51
46 const VAddr addr; 52 CacheAddr addr;
47 const u64 size; 53 u64 size;
48}; 54};
49 55
50/// Command to signal to the GPU thread to invalidate a region 56/// Command to signal to the GPU thread to invalidate a region
51struct InvalidateRegionCommand final { 57struct InvalidateRegionCommand final {
52 explicit constexpr InvalidateRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {} 58 explicit constexpr InvalidateRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {}
53 59
54 const VAddr addr; 60 CacheAddr addr;
55 const u64 size; 61 u64 size;
56}; 62};
57 63
58/// Command to signal to the GPU thread to flush and invalidate a region 64/// Command to signal to the GPU thread to flush and invalidate a region
59struct FlushAndInvalidateRegionCommand final { 65struct FlushAndInvalidateRegionCommand final {
60 explicit constexpr FlushAndInvalidateRegionCommand(VAddr addr, u64 size) 66 explicit constexpr FlushAndInvalidateRegionCommand(CacheAddr addr, u64 size)
61 : addr{addr}, size{size} {} 67 : addr{addr}, size{size} {}
62 68
63 const VAddr addr; 69 CacheAddr addr;
64 const u64 size; 70 u64 size;
65}; 71};
66 72
67using CommandData = std::variant<SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, 73using CommandData =
68 InvalidateRegionCommand, FlushAndInvalidateRegionCommand>; 74 std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
75 InvalidateRegionCommand, FlushAndInvalidateRegionCommand>;
76
77struct CommandDataContainer {
78 CommandDataContainer() = default;
79
80 CommandDataContainer(CommandData&& data) : data{std::move(data)} {}
81
82 CommandDataContainer& operator=(const CommandDataContainer& t) {
83 data = std::move(t.data);
84 return *this;
85 }
86
87 CommandData data;
88};
69 89
70/// Struct used to synchronize the GPU thread 90/// Struct used to synchronize the GPU thread
71struct SynchState final { 91struct SynchState final {
72 std::atomic<bool> is_running{true}; 92 std::atomic_bool is_running{true};
73 std::atomic<bool> is_idle{true}; 93 std::atomic_int queued_frame_count{};
74 std::condition_variable signal_condition; 94 std::mutex frames_mutex;
75 std::mutex signal_mutex; 95 std::mutex commands_mutex;
76 std::condition_variable idle_condition; 96 std::condition_variable commands_condition;
77 std::mutex idle_mutex; 97 std::condition_variable frames_condition;
78 98
79 // We use two queues for sending commands to the GPU thread, one for writing (push_queue) to and 99 void IncrementFramesCounter() {
80 // one for reading from (pop_queue). These are swapped whenever the current pop_queue becomes 100 std::lock_guard<std::mutex> lock{frames_mutex};
81 // empty. This allows for efficient thread-safe access, as it does not require any copies. 101 ++queued_frame_count;
82 102 }
83 using CommandQueue = std::queue<CommandData>; 103
84 std::array<CommandQueue, 2> command_queues; 104 void DecrementFramesCounter() {
85 CommandQueue* push_queue{&command_queues[0]}; 105 {
86 CommandQueue* pop_queue{&command_queues[1]}; 106 std::lock_guard<std::mutex> lock{frames_mutex};
87 107 --queued_frame_count;
88 void UpdateIdleState() { 108
89 std::lock_guard<std::mutex> lock{idle_mutex}; 109 if (queued_frame_count) {
90 is_idle = command_queues[0].empty() && command_queues[1].empty(); 110 return;
111 }
112 }
113 frames_condition.notify_one();
91 } 114 }
115
116 void WaitForFrames() {
117 {
118 std::lock_guard<std::mutex> lock{frames_mutex};
119 if (!queued_frame_count) {
120 return;
121 }
122 }
123
124 // Wait for the GPU to be idle (all commands to be executed)
125 {
126 std::unique_lock<std::mutex> lock{frames_mutex};
127 frames_condition.wait(lock, [this] { return !queued_frame_count; });
128 }
129 }
130
131 void SignalCommands() {
132 {
133 std::unique_lock<std::mutex> lock{commands_mutex};
134 if (queue.Empty()) {
135 return;
136 }
137 }
138
139 commands_condition.notify_one();
140 }
141
142 void WaitForCommands() {
143 std::unique_lock<std::mutex> lock{commands_mutex};
144 commands_condition.wait(lock, [this] { return !queue.Empty(); });
145 }
146
147 using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
148 CommandQueue queue;
92}; 149};
93 150
94/// Class used to manage the GPU thread 151/// Class used to manage the GPU thread
@@ -105,22 +162,17 @@ public:
105 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer); 162 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer);
106 163
107 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 164 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
108 void FlushRegion(VAddr addr, u64 size); 165 void FlushRegion(CacheAddr addr, u64 size);
109 166
110 /// Notify rasterizer that any caches of the specified region should be invalidated 167 /// Notify rasterizer that any caches of the specified region should be invalidated
111 void InvalidateRegion(VAddr addr, u64 size); 168 void InvalidateRegion(CacheAddr addr, u64 size);
112 169
113 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated 170 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
114 void FlushAndInvalidateRegion(VAddr addr, u64 size); 171 void FlushAndInvalidateRegion(CacheAddr addr, u64 size);
115 172
116private: 173private:
117 /// Pushes a command to be executed by the GPU thread 174 /// Pushes a command to be executed by the GPU thread
118 void PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu); 175 void 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 176
125private: 177private:
126 SynchState state; 178 SynchState state;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 54abe5298..8e8f36f28 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -5,6 +5,7 @@
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/memory.h"
8#include "video_core/memory_manager.h" 9#include "video_core/memory_manager.h"
9 10
10namespace Tegra { 11namespace Tegra {
@@ -162,15 +163,51 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) {
162 return base_addr + (gpu_addr & PAGE_MASK); 163 return base_addr + (gpu_addr & PAGE_MASK);
163} 164}
164 165
165std::vector<GPUVAddr> MemoryManager::CpuToGpuAddress(VAddr cpu_addr) const { 166u8 MemoryManager::Read8(GPUVAddr addr) {
166 std::vector<GPUVAddr> results; 167 return Memory::Read8(*GpuToCpuAddress(addr));
167 for (const auto& region : mapped_regions) { 168}
168 if (cpu_addr >= region.cpu_addr && cpu_addr < (region.cpu_addr + region.size)) { 169
169 const u64 offset{cpu_addr - region.cpu_addr}; 170u16 MemoryManager::Read16(GPUVAddr addr) {
170 results.push_back(region.gpu_addr + offset); 171 return Memory::Read16(*GpuToCpuAddress(addr));
171 } 172}
172 } 173
173 return results; 174u32 MemoryManager::Read32(GPUVAddr addr) {
175 return Memory::Read32(*GpuToCpuAddress(addr));
176}
177
178u64 MemoryManager::Read64(GPUVAddr addr) {
179 return Memory::Read64(*GpuToCpuAddress(addr));
180}
181
182void MemoryManager::Write8(GPUVAddr addr, u8 data) {
183 Memory::Write8(*GpuToCpuAddress(addr), data);
184}
185
186void MemoryManager::Write16(GPUVAddr addr, u16 data) {
187 Memory::Write16(*GpuToCpuAddress(addr), data);
188}
189
190void MemoryManager::Write32(GPUVAddr addr, u32 data) {
191 Memory::Write32(*GpuToCpuAddress(addr), data);
192}
193
194void MemoryManager::Write64(GPUVAddr addr, u64 data) {
195 Memory::Write64(*GpuToCpuAddress(addr), data);
196}
197
198u8* MemoryManager::GetPointer(GPUVAddr addr) {
199 return Memory::GetPointer(*GpuToCpuAddress(addr));
200}
201
202void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) {
203 std::memcpy(dest_buffer, GetPointer(src_addr), size);
204}
205void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) {
206 std::memcpy(GetPointer(dest_addr), src_buffer, size);
207}
208
209void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) {
210 std::memcpy(GetPointer(dest_addr), GetPointer(src_addr), size);
174} 211}
175 212
176VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) { 213VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) {
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index fb03497ca..425e2f31c 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -27,12 +27,27 @@ public:
27 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); 27 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
28 GPUVAddr GetRegionEnd(GPUVAddr region_start) const; 28 GPUVAddr GetRegionEnd(GPUVAddr region_start) const;
29 std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); 29 std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
30 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
31 30
32 static constexpr u64 PAGE_BITS = 16; 31 static constexpr u64 PAGE_BITS = 16;
33 static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; 32 static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS;
34 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 33 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
35 34
35 u8 Read8(GPUVAddr addr);
36 u16 Read16(GPUVAddr addr);
37 u32 Read32(GPUVAddr addr);
38 u64 Read64(GPUVAddr addr);
39
40 void Write8(GPUVAddr addr, u8 data);
41 void Write16(GPUVAddr addr, u16 data);
42 void Write32(GPUVAddr addr, u32 data);
43 void Write64(GPUVAddr addr, u64 data);
44
45 u8* GetPointer(GPUVAddr vaddr);
46
47 void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size);
48 void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size);
49 void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
50
36private: 51private:
37 enum class PageStatus : u64 { 52 enum class PageStatus : u64 {
38 Unmapped = 0xFFFFFFFFFFFFFFFFULL, 53 Unmapped = 0xFFFFFFFFFFFFFFFFULL,
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
index b68f4fb13..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*, std::size_t, 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, std::size_t buffer_size, 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,150 +33,146 @@ 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
44static constexpr ConversionArray morton_to_linear_fns = { 43static constexpr ConversionArray morton_to_linear_fns = {
45 // clang-format off 44 MortonCopy<true, PixelFormat::ABGR8U>,
46 MortonCopy<true, PixelFormat::ABGR8U>, 45 MortonCopy<true, PixelFormat::ABGR8S>,
47 MortonCopy<true, PixelFormat::ABGR8S>, 46 MortonCopy<true, PixelFormat::ABGR8UI>,
48 MortonCopy<true, PixelFormat::ABGR8UI>, 47 MortonCopy<true, PixelFormat::B5G6R5U>,
49 MortonCopy<true, PixelFormat::B5G6R5U>, 48 MortonCopy<true, PixelFormat::A2B10G10R10U>,
50 MortonCopy<true, PixelFormat::A2B10G10R10U>, 49 MortonCopy<true, PixelFormat::A1B5G5R5U>,
51 MortonCopy<true, PixelFormat::A1B5G5R5U>, 50 MortonCopy<true, PixelFormat::R8U>,
52 MortonCopy<true, PixelFormat::R8U>, 51 MortonCopy<true, PixelFormat::R8UI>,
53 MortonCopy<true, PixelFormat::R8UI>, 52 MortonCopy<true, PixelFormat::RGBA16F>,
54 MortonCopy<true, PixelFormat::RGBA16F>, 53 MortonCopy<true, PixelFormat::RGBA16U>,
55 MortonCopy<true, PixelFormat::RGBA16U>, 54 MortonCopy<true, PixelFormat::RGBA16UI>,
56 MortonCopy<true, PixelFormat::RGBA16UI>, 55 MortonCopy<true, PixelFormat::R11FG11FB10F>,
57 MortonCopy<true, PixelFormat::R11FG11FB10F>, 56 MortonCopy<true, PixelFormat::RGBA32UI>,
58 MortonCopy<true, PixelFormat::RGBA32UI>, 57 MortonCopy<true, PixelFormat::DXT1>,
59 MortonCopy<true, PixelFormat::DXT1>, 58 MortonCopy<true, PixelFormat::DXT23>,
60 MortonCopy<true, PixelFormat::DXT23>, 59 MortonCopy<true, PixelFormat::DXT45>,
61 MortonCopy<true, PixelFormat::DXT45>, 60 MortonCopy<true, PixelFormat::DXN1>,
62 MortonCopy<true, PixelFormat::DXN1>, 61 MortonCopy<true, PixelFormat::DXN2UNORM>,
63 MortonCopy<true, PixelFormat::DXN2UNORM>, 62 MortonCopy<true, PixelFormat::DXN2SNORM>,
64 MortonCopy<true, PixelFormat::DXN2SNORM>, 63 MortonCopy<true, PixelFormat::BC7U>,
65 MortonCopy<true, PixelFormat::BC7U>, 64 MortonCopy<true, PixelFormat::BC6H_UF16>,
66 MortonCopy<true, PixelFormat::BC6H_UF16>, 65 MortonCopy<true, PixelFormat::BC6H_SF16>,
67 MortonCopy<true, PixelFormat::BC6H_SF16>, 66 MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
68 MortonCopy<true, PixelFormat::ASTC_2D_4X4>, 67 MortonCopy<true, PixelFormat::BGRA8>,
69 MortonCopy<true, PixelFormat::BGRA8>, 68 MortonCopy<true, PixelFormat::RGBA32F>,
70 MortonCopy<true, PixelFormat::RGBA32F>, 69 MortonCopy<true, PixelFormat::RG32F>,
71 MortonCopy<true, PixelFormat::RG32F>, 70 MortonCopy<true, PixelFormat::R32F>,
72 MortonCopy<true, PixelFormat::R32F>, 71 MortonCopy<true, PixelFormat::R16F>,
73 MortonCopy<true, PixelFormat::R16F>, 72 MortonCopy<true, PixelFormat::R16U>,
74 MortonCopy<true, PixelFormat::R16U>, 73 MortonCopy<true, PixelFormat::R16S>,
75 MortonCopy<true, PixelFormat::R16S>, 74 MortonCopy<true, PixelFormat::R16UI>,
76 MortonCopy<true, PixelFormat::R16UI>, 75 MortonCopy<true, PixelFormat::R16I>,
77 MortonCopy<true, PixelFormat::R16I>, 76 MortonCopy<true, PixelFormat::RG16>,
78 MortonCopy<true, PixelFormat::RG16>, 77 MortonCopy<true, PixelFormat::RG16F>,
79 MortonCopy<true, PixelFormat::RG16F>, 78 MortonCopy<true, PixelFormat::RG16UI>,
80 MortonCopy<true, PixelFormat::RG16UI>, 79 MortonCopy<true, PixelFormat::RG16I>,
81 MortonCopy<true, PixelFormat::RG16I>, 80 MortonCopy<true, PixelFormat::RG16S>,
82 MortonCopy<true, PixelFormat::RG16S>, 81 MortonCopy<true, PixelFormat::RGB32F>,
83 MortonCopy<true, PixelFormat::RGB32F>, 82 MortonCopy<true, PixelFormat::RGBA8_SRGB>,
84 MortonCopy<true, PixelFormat::RGBA8_SRGB>, 83 MortonCopy<true, PixelFormat::RG8U>,
85 MortonCopy<true, PixelFormat::RG8U>, 84 MortonCopy<true, PixelFormat::RG8S>,
86 MortonCopy<true, PixelFormat::RG8S>, 85 MortonCopy<true, PixelFormat::RG32UI>,
87 MortonCopy<true, PixelFormat::RG32UI>, 86 MortonCopy<true, PixelFormat::R32UI>,
88 MortonCopy<true, PixelFormat::R32UI>, 87 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
89 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 88 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
90 MortonCopy<true, PixelFormat::ASTC_2D_8X5>, 89 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
91 MortonCopy<true, PixelFormat::ASTC_2D_5X4>, 90 MortonCopy<true, PixelFormat::BGRA8_SRGB>,
92 MortonCopy<true, PixelFormat::BGRA8_SRGB>, 91 MortonCopy<true, PixelFormat::DXT1_SRGB>,
93 MortonCopy<true, PixelFormat::DXT1_SRGB>, 92 MortonCopy<true, PixelFormat::DXT23_SRGB>,
94 MortonCopy<true, PixelFormat::DXT23_SRGB>, 93 MortonCopy<true, PixelFormat::DXT45_SRGB>,
95 MortonCopy<true, PixelFormat::DXT45_SRGB>, 94 MortonCopy<true, PixelFormat::BC7U_SRGB>,
96 MortonCopy<true, PixelFormat::BC7U_SRGB>, 95 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>,
97 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>, 96 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
98 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>, 97 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
99 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>, 98 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
100 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, 99 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
101 MortonCopy<true, PixelFormat::ASTC_2D_5X5>, 100 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
102 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>, 101 MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
103 MortonCopy<true, PixelFormat::ASTC_2D_10X8>, 102 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
104 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>, 103 MortonCopy<true, PixelFormat::Z32F>,
105 MortonCopy<true, PixelFormat::Z32F>, 104 MortonCopy<true, PixelFormat::Z16>,
106 MortonCopy<true, PixelFormat::Z16>, 105 MortonCopy<true, PixelFormat::Z24S8>,
107 MortonCopy<true, PixelFormat::Z24S8>, 106 MortonCopy<true, PixelFormat::S8Z24>,
108 MortonCopy<true, PixelFormat::S8Z24>, 107 MortonCopy<true, PixelFormat::Z32FS8>,
109 MortonCopy<true, PixelFormat::Z32FS8>,
110 // clang-format on
111}; 108};
112 109
113static constexpr ConversionArray linear_to_morton_fns = { 110static constexpr ConversionArray linear_to_morton_fns = {
114 // clang-format off 111 MortonCopy<false, PixelFormat::ABGR8U>,
115 MortonCopy<false, PixelFormat::ABGR8U>, 112 MortonCopy<false, PixelFormat::ABGR8S>,
116 MortonCopy<false, PixelFormat::ABGR8S>, 113 MortonCopy<false, PixelFormat::ABGR8UI>,
117 MortonCopy<false, PixelFormat::ABGR8UI>, 114 MortonCopy<false, PixelFormat::B5G6R5U>,
118 MortonCopy<false, PixelFormat::B5G6R5U>, 115 MortonCopy<false, PixelFormat::A2B10G10R10U>,
119 MortonCopy<false, PixelFormat::A2B10G10R10U>, 116 MortonCopy<false, PixelFormat::A1B5G5R5U>,
120 MortonCopy<false, PixelFormat::A1B5G5R5U>, 117 MortonCopy<false, PixelFormat::R8U>,
121 MortonCopy<false, PixelFormat::R8U>, 118 MortonCopy<false, PixelFormat::R8UI>,
122 MortonCopy<false, PixelFormat::R8UI>, 119 MortonCopy<false, PixelFormat::RGBA16F>,
123 MortonCopy<false, PixelFormat::RGBA16F>, 120 MortonCopy<false, PixelFormat::RGBA16U>,
124 MortonCopy<false, PixelFormat::RGBA16U>, 121 MortonCopy<false, PixelFormat::RGBA16UI>,
125 MortonCopy<false, PixelFormat::RGBA16UI>, 122 MortonCopy<false, PixelFormat::R11FG11FB10F>,
126 MortonCopy<false, PixelFormat::R11FG11FB10F>, 123 MortonCopy<false, PixelFormat::RGBA32UI>,
127 MortonCopy<false, PixelFormat::RGBA32UI>, 124 MortonCopy<false, PixelFormat::DXT1>,
128 MortonCopy<false, PixelFormat::DXT1>, 125 MortonCopy<false, PixelFormat::DXT23>,
129 MortonCopy<false, PixelFormat::DXT23>, 126 MortonCopy<false, PixelFormat::DXT45>,
130 MortonCopy<false, PixelFormat::DXT45>, 127 MortonCopy<false, PixelFormat::DXN1>,
131 MortonCopy<false, PixelFormat::DXN1>, 128 MortonCopy<false, PixelFormat::DXN2UNORM>,
132 MortonCopy<false, PixelFormat::DXN2UNORM>, 129 MortonCopy<false, PixelFormat::DXN2SNORM>,
133 MortonCopy<false, PixelFormat::DXN2SNORM>, 130 MortonCopy<false, PixelFormat::BC7U>,
134 MortonCopy<false, PixelFormat::BC7U>, 131 MortonCopy<false, PixelFormat::BC6H_UF16>,
135 MortonCopy<false, PixelFormat::BC6H_UF16>, 132 MortonCopy<false, PixelFormat::BC6H_SF16>,
136 MortonCopy<false, PixelFormat::BC6H_SF16>, 133 // TODO(Subv): Swizzling ASTC formats are not supported
137 // TODO(Subv): Swizzling ASTC formats are not supported 134 nullptr,
138 nullptr, 135 MortonCopy<false, PixelFormat::BGRA8>,
139 MortonCopy<false, PixelFormat::BGRA8>, 136 MortonCopy<false, PixelFormat::RGBA32F>,
140 MortonCopy<false, PixelFormat::RGBA32F>, 137 MortonCopy<false, PixelFormat::RG32F>,
141 MortonCopy<false, PixelFormat::RG32F>, 138 MortonCopy<false, PixelFormat::R32F>,
142 MortonCopy<false, PixelFormat::R32F>, 139 MortonCopy<false, PixelFormat::R16F>,
143 MortonCopy<false, PixelFormat::R16F>, 140 MortonCopy<false, PixelFormat::R16U>,
144 MortonCopy<false, PixelFormat::R16U>, 141 MortonCopy<false, PixelFormat::R16S>,
145 MortonCopy<false, PixelFormat::R16S>, 142 MortonCopy<false, PixelFormat::R16UI>,
146 MortonCopy<false, PixelFormat::R16UI>, 143 MortonCopy<false, PixelFormat::R16I>,
147 MortonCopy<false, PixelFormat::R16I>, 144 MortonCopy<false, PixelFormat::RG16>,
148 MortonCopy<false, PixelFormat::RG16>, 145 MortonCopy<false, PixelFormat::RG16F>,
149 MortonCopy<false, PixelFormat::RG16F>, 146 MortonCopy<false, PixelFormat::RG16UI>,
150 MortonCopy<false, PixelFormat::RG16UI>, 147 MortonCopy<false, PixelFormat::RG16I>,
151 MortonCopy<false, PixelFormat::RG16I>, 148 MortonCopy<false, PixelFormat::RG16S>,
152 MortonCopy<false, PixelFormat::RG16S>, 149 MortonCopy<false, PixelFormat::RGB32F>,
153 MortonCopy<false, PixelFormat::RGB32F>, 150 MortonCopy<false, PixelFormat::RGBA8_SRGB>,
154 MortonCopy<false, PixelFormat::RGBA8_SRGB>, 151 MortonCopy<false, PixelFormat::RG8U>,
155 MortonCopy<false, PixelFormat::RG8U>, 152 MortonCopy<false, PixelFormat::RG8S>,
156 MortonCopy<false, PixelFormat::RG8S>, 153 MortonCopy<false, PixelFormat::RG32UI>,
157 MortonCopy<false, PixelFormat::RG32UI>, 154 MortonCopy<false, PixelFormat::R32UI>,
158 MortonCopy<false, PixelFormat::R32UI>, 155 nullptr,
159 nullptr, 156 nullptr,
160 nullptr, 157 nullptr,
161 nullptr, 158 MortonCopy<false, PixelFormat::BGRA8_SRGB>,
162 MortonCopy<false, PixelFormat::BGRA8_SRGB>, 159 MortonCopy<false, PixelFormat::DXT1_SRGB>,
163 MortonCopy<false, PixelFormat::DXT1_SRGB>, 160 MortonCopy<false, PixelFormat::DXT23_SRGB>,
164 MortonCopy<false, PixelFormat::DXT23_SRGB>, 161 MortonCopy<false, PixelFormat::DXT45_SRGB>,
165 MortonCopy<false, PixelFormat::DXT45_SRGB>, 162 MortonCopy<false, PixelFormat::BC7U_SRGB>,
166 MortonCopy<false, PixelFormat::BC7U_SRGB>, 163 nullptr,
167 nullptr, 164 nullptr,
168 nullptr, 165 nullptr,
169 nullptr, 166 nullptr,
170 nullptr, 167 nullptr,
171 nullptr, 168 nullptr,
172 nullptr, 169 nullptr,
173 nullptr, 170 nullptr,
174 nullptr, 171 MortonCopy<false, PixelFormat::Z32F>,
175 MortonCopy<false, PixelFormat::Z32F>, 172 MortonCopy<false, PixelFormat::Z16>,
176 MortonCopy<false, PixelFormat::Z16>, 173 MortonCopy<false, PixelFormat::Z24S8>,
177 MortonCopy<false, PixelFormat::Z24S8>, 174 MortonCopy<false, PixelFormat::S8Z24>,
178 MortonCopy<false, PixelFormat::S8Z24>, 175 MortonCopy<false, PixelFormat::Z32FS8>,
179 MortonCopy<false, PixelFormat::Z32FS8>,
180 // clang-format on
181}; 176};
182 177
183static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) { 178static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) {
@@ -191,45 +186,6 @@ static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFor
191 return morton_to_linear_fns[static_cast<std::size_t>(format)]; 186 return morton_to_linear_fns[static_cast<std::size_t>(format)];
192} 187}
193 188
194/// 8x8 Z-Order coordinate from 2D coordinates
195static u32 MortonInterleave(u32 x, u32 y) {
196 static const u32 xlut[] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15};
197 static const u32 ylut[] = {0x00, 0x02, 0x08, 0x0a, 0x20, 0x22, 0x28, 0x2a};
198 return xlut[x % 8] + ylut[y % 8];
199}
200
201/// Calculates the offset of the position of the pixel in Morton order
202static u32 GetMortonOffset(u32 x, u32 y, u32 bytes_per_pixel) {
203 // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each
204 // of which is composed of four 2x2 subtiles each of which is composed of four texels.
205 // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g.
206 // texels are laid out in a 2x2 subtile like this:
207 // 2 3
208 // 0 1
209 //
210 // The full 8x8 tile has the texels arranged like this:
211 //
212 // 42 43 46 47 58 59 62 63
213 // 40 41 44 45 56 57 60 61
214 // 34 35 38 39 50 51 54 55
215 // 32 33 36 37 48 49 52 53
216 // 10 11 14 15 26 27 30 31
217 // 08 09 12 13 24 25 28 29
218 // 02 03 06 07 18 19 22 23
219 // 00 01 04 05 16 17 20 21
220 //
221 // This pattern is what's called Z-order curve, or Morton order.
222
223 const unsigned int block_height = 8;
224 const unsigned int coarse_x = x & ~7;
225
226 u32 i = MortonInterleave(x, y);
227
228 const unsigned int offset = coarse_x * block_height;
229
230 return (i + offset) * bytes_per_pixel;
231}
232
233static u32 MortonInterleave128(u32 x, u32 y) { 189static u32 MortonInterleave128(u32 x, u32 y) {
234 // 128x128 Z-Order coordinate from 2D coordinates 190 // 128x128 Z-Order coordinate from 2D coordinates
235 static constexpr u32 xlut[] = { 191 static constexpr u32 xlut[] = {
@@ -325,14 +281,14 @@ static u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) {
325 281
326void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride, 282void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride,
327 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,
328 u8* buffer, std::size_t buffer_size, VAddr addr) { 284 u8* buffer, u8* addr) {
329
330 GetSwizzleFunction(mode, format)(stride, block_height, height, block_depth, depth, 285 GetSwizzleFunction(mode, format)(stride, block_height, height, block_depth, depth,
331 tile_width_spacing, buffer, buffer_size, addr); 286 tile_width_spacing, buffer, addr);
332} 287}
333 288
334void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32 linear_bytes_per_pixel, 289void MortonCopyPixels128(MortonSwizzleMode mode, u32 width, u32 height, u32 bytes_per_pixel,
335 u8* morton_data, u8* linear_data, bool morton_to_linear) { 290 u32 linear_bytes_per_pixel, u8* morton_data, u8* linear_data) {
291 const bool morton_to_linear = mode == MortonSwizzleMode::MortonToLinear;
336 u8* data_ptrs[2]; 292 u8* data_ptrs[2];
337 for (u32 y = 0; y < height; ++y) { 293 for (u32 y = 0; y < height; ++y) {
338 for (u32 x = 0; x < width; ++x) { 294 for (u32 x = 0; x < width; ++x) {
diff --git a/src/video_core/morton.h b/src/video_core/morton.h
index 065f59ce3..ee5b45555 100644
--- a/src/video_core/morton.h
+++ b/src/video_core/morton.h
@@ -13,9 +13,9 @@ 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, std::size_t buffer_size, VAddr addr); 16 u8* buffer, u8* addr);
17 17
18void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32 linear_bytes_per_pixel, 18void MortonCopyPixels128(MortonSwizzleMode mode, u32 width, u32 height, u32 bytes_per_pixel,
19 u8* morton_data, u8* linear_data, bool morton_to_linear); 19 u32 linear_bytes_per_pixel, u8* morton_data, u8* linear_data);
20 20
21} // namespace VideoCore 21} // namespace VideoCore
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index a7bcf26fb..ecd9986a0 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 CacheAddr cache_addr{}; ///< Cache address memory, unique from emulated virtual address space
75 const u8* host_ptr{}; ///< Pointer to the memory backing this cached region
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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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 void Register(const T& object) {
136 std::lock_guard<std::recursive_mutex> 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 void Unregister(const T& object) {
116 object->SetIsRegistered(false); 146 std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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..76e292e87 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -35,14 +35,14 @@ public:
35 virtual void FlushAll() = 0; 35 virtual void FlushAll() = 0;
36 36
37 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 37 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
38 virtual void FlushRegion(VAddr addr, u64 size) = 0; 38 virtual void FlushRegion(CacheAddr addr, u64 size) = 0;
39 39
40 /// Notify rasterizer that any caches of the specified region should be invalidated 40 /// Notify rasterizer that any caches of the specified region should be invalidated
41 virtual void InvalidateRegion(VAddr addr, u64 size) = 0; 41 virtual void InvalidateRegion(CacheAddr addr, u64 size) = 0;
42 42
43 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 43 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
44 /// and invalidated 44 /// and invalidated
45 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 45 virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0;
46 46
47 /// Attempt to use a faster method to perform a surface copy 47 /// Attempt to use a faster method to perform a surface copy
48 virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, 48 virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
@@ -63,7 +63,7 @@ public:
63 } 63 }
64 64
65 /// Increase/decrease the number of object in pages touching the specified region 65 /// Increase/decrease the number of object in pages touching the specified region
66 virtual void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) {} 66 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {}
67 67
68 /// Initialize disk cached resources for the game being emulated 68 /// Initialize disk cached resources for the game being emulated
69 virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, 69 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..5048ed6ce 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -13,24 +13,28 @@
13 13
14namespace OpenGL { 14namespace OpenGL {
15 15
16CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, GLintptr offset,
17 std::size_t alignment, u8* host_ptr)
18 : cpu_addr{cpu_addr}, size{size}, offset{offset}, alignment{alignment}, RasterizerCacheObject{
19 host_ptr} {}
20
16OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size) 21OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
17 : RasterizerCache{rasterizer}, stream_buffer(size, true) {} 22 : RasterizerCache{rasterizer}, stream_buffer(size, true) {}
18 23
19GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, 24GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
20 std::size_t alignment, bool cache) { 25 std::size_t alignment, bool cache) {
21 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 26 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 27
25 // Cache management is a big overhead, so only cache entries with a given size. 28 // 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. 29 // TODO: Figure out which size is the best for given games.
27 cache &= size >= 2048; 30 cache &= size >= 2048;
28 31
32 const auto& host_ptr{memory_manager.GetPointer(gpu_addr)};
29 if (cache) { 33 if (cache) {
30 auto entry = TryGet(*cpu_addr); 34 auto entry = TryGet(host_ptr);
31 if (entry) { 35 if (entry) {
32 if (entry->size >= size && entry->alignment == alignment) { 36 if (entry->GetSize() >= size && entry->GetAlignment() == alignment) {
33 return entry->offset; 37 return entry->GetOffset();
34 } 38 }
35 Unregister(entry); 39 Unregister(entry);
36 } 40 }
@@ -39,17 +43,17 @@ GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size
39 AlignBuffer(alignment); 43 AlignBuffer(alignment);
40 const GLintptr uploaded_offset = buffer_offset; 44 const GLintptr uploaded_offset = buffer_offset;
41 45
42 Memory::ReadBlock(*cpu_addr, buffer_ptr, size); 46 if (!host_ptr) {
47 return uploaded_offset;
48 }
43 49
50 std::memcpy(buffer_ptr, host_ptr, size);
44 buffer_ptr += size; 51 buffer_ptr += size;
45 buffer_offset += size; 52 buffer_offset += size;
46 53
47 if (cache) { 54 if (cache) {
48 auto entry = std::make_shared<CachedBufferEntry>(); 55 auto entry = std::make_shared<CachedBufferEntry>(
49 entry->offset = uploaded_offset; 56 *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); 57 Register(entry);
54 } 58 }
55 59
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index c11acfb79..1de1f84ae 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>> {
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp
index 7161d1dea..c8dbcacbd 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_global_cache.cpp
@@ -7,7 +7,6 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/memory.h"
11#include "video_core/renderer_opengl/gl_global_cache.h" 10#include "video_core/renderer_opengl/gl_global_cache.h"
12#include "video_core/renderer_opengl/gl_rasterizer.h" 11#include "video_core/renderer_opengl/gl_rasterizer.h"
13#include "video_core/renderer_opengl/gl_shader_decompiler.h" 12#include "video_core/renderer_opengl/gl_shader_decompiler.h"
@@ -15,12 +14,13 @@
15 14
16namespace OpenGL { 15namespace OpenGL {
17 16
18CachedGlobalRegion::CachedGlobalRegion(VAddr addr, u32 size) : addr{addr}, size{size} { 17CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr)
18 : cpu_addr{cpu_addr}, size{size}, RasterizerCacheObject{host_ptr} {
19 buffer.Create(); 19 buffer.Create();
20 // Bind and unbind the buffer so it gets allocated by the driver 20 // Bind and unbind the buffer so it gets allocated by the driver
21 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); 21 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
22 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 22 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
23 LabelGLObject(GL_BUFFER, buffer.handle, addr, "GlobalMemory"); 23 LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory");
24} 24}
25 25
26void CachedGlobalRegion::Reload(u32 size_) { 26void CachedGlobalRegion::Reload(u32 size_) {
@@ -35,10 +35,10 @@ void CachedGlobalRegion::Reload(u32 size_) {
35 35
36 // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer 36 // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer
37 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); 37 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
38 glBufferData(GL_SHADER_STORAGE_BUFFER, size, Memory::GetPointer(addr), GL_DYNAMIC_DRAW); 38 glBufferData(GL_SHADER_STORAGE_BUFFER, size, GetHostPtr(), GL_DYNAMIC_DRAW);
39} 39}
40 40
41GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32 size) const { 41GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const {
42 const auto search{reserve.find(addr)}; 42 const auto search{reserve.find(addr)};
43 if (search == reserve.end()) { 43 if (search == reserve.end()) {
44 return {}; 44 return {};
@@ -46,11 +46,14 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32
46 return search->second; 46 return search->second;
47} 47}
48 48
49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 size) { 49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(Tegra::GPUVAddr addr, u32 size,
50 GlobalRegion region{TryGetReservedGlobalRegion(addr, size)}; 50 u8* host_ptr) {
51 GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)};
51 if (!region) { 52 if (!region) {
52 // No reserved surface available, create a new one and reserve it 53 // No reserved surface available, create a new one and reserve it
53 region = std::make_shared<CachedGlobalRegion>(addr, size); 54 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
55 const auto cpu_addr = *memory_manager.GpuToCpuAddress(addr);
56 region = std::make_shared<CachedGlobalRegion>(cpu_addr, size, host_ptr);
54 ReserveGlobalRegion(region); 57 ReserveGlobalRegion(region);
55 } 58 }
56 region->Reload(size); 59 region->Reload(size);
@@ -58,7 +61,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 si
58} 61}
59 62
60void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) { 63void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) {
61 reserve.insert_or_assign(region->GetAddr(), std::move(region)); 64 reserve.insert_or_assign(region->GetCacheAddr(), std::move(region));
62} 65}
63 66
64GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer) 67GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
@@ -69,22 +72,20 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
69 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) { 72 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) {
70 73
71 auto& gpu{Core::System::GetInstance().GPU()}; 74 auto& gpu{Core::System::GetInstance().GPU()};
72 const auto cbufs = gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]; 75 auto& memory_manager{gpu.MemoryManager()};
73 const auto cbuf_addr = gpu.MemoryManager().GpuToCpuAddress( 76 const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]};
74 cbufs.const_buffers[global_region.GetCbufIndex()].address + global_region.GetCbufOffset()); 77 const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address +
75 ASSERT(cbuf_addr); 78 global_region.GetCbufOffset()};
76 79 const auto actual_addr{memory_manager.Read64(addr)};
77 const auto actual_addr_gpu = Memory::Read64(*cbuf_addr); 80 const auto size{memory_manager.Read32(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 81
82 // Look up global region in the cache based on address 82 // Look up global region in the cache based on address
83 GlobalRegion region = TryGet(*actual_addr); 83 const auto& host_ptr{memory_manager.GetPointer(actual_addr)};
84 GlobalRegion region{TryGet(host_ptr)};
84 85
85 if (!region) { 86 if (!region) {
86 // No global region found - create a new one 87 // No global region found - create a new one
87 region = GetUncachedGlobalRegion(*actual_addr, size); 88 region = GetUncachedGlobalRegion(actual_addr, size, host_ptr);
88 Register(region); 89 Register(region);
89 } 90 }
90 91
diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h
index ba2bdc60c..a840491f7 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(Tegra::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..75d816795 100644
--- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
+++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
@@ -46,10 +46,7 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size
46 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); 46 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size);
47 47
48 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 48 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
49 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; 49 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 50
54 for (u32 primitive = 0; primitive < count / 4; ++primitive) { 51 for (u32 primitive = 0; primitive < count / 4; ++primitive) {
55 for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) { 52 for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) {
@@ -64,4 +61,4 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size
64 return index_offset; 61 return index_offset;
65} 62}
66 63
67} // namespace OpenGL \ No newline at end of file 64} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 976f64c24..198c54872 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -449,7 +449,7 @@ static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
449 return boost::make_iterator_range(map.equal_range(interval)); 449 return boost::make_iterator_range(map.equal_range(interval));
450} 450}
451 451
452void RasterizerOpenGL::UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) { 452void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
453 const u64 page_start{addr >> Memory::PAGE_BITS}; 453 const u64 page_start{addr >> Memory::PAGE_BITS};
454 const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS}; 454 const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS};
455 455
@@ -747,20 +747,26 @@ void RasterizerOpenGL::DrawArrays() {
747 747
748void RasterizerOpenGL::FlushAll() {} 748void RasterizerOpenGL::FlushAll() {}
749 749
750void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { 750void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) {
751 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 751 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
752 if (!addr || !size) {
753 return;
754 }
752 res_cache.FlushRegion(addr, size); 755 res_cache.FlushRegion(addr, size);
753} 756}
754 757
755void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 758void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) {
756 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 759 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
760 if (!addr || !size) {
761 return;
762 }
757 res_cache.InvalidateRegion(addr, size); 763 res_cache.InvalidateRegion(addr, size);
758 shader_cache.InvalidateRegion(addr, size); 764 shader_cache.InvalidateRegion(addr, size);
759 global_cache.InvalidateRegion(addr, size); 765 global_cache.InvalidateRegion(addr, size);
760 buffer_cache.InvalidateRegion(addr, size); 766 buffer_cache.InvalidateRegion(addr, size);
761} 767}
762 768
763void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 769void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
764 FlushRegion(addr, size); 770 FlushRegion(addr, size);
765 InvalidateRegion(addr, size); 771 InvalidateRegion(addr, size);
766} 772}
@@ -782,7 +788,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
782 788
783 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 789 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
784 790
785 const auto& surface{res_cache.TryFindFramebufferSurface(framebuffer_addr)}; 791 const auto& surface{res_cache.TryFindFramebufferSurface(Memory::GetPointer(framebuffer_addr))};
786 if (!surface) { 792 if (!surface) {
787 return {}; 793 return {};
788 } 794 }
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index ca3de0592..30f3e8acb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -57,9 +57,9 @@ public:
57 void DrawArrays() override; 57 void DrawArrays() override;
58 void Clear() override; 58 void Clear() override;
59 void FlushAll() override; 59 void FlushAll() override;
60 void FlushRegion(VAddr addr, u64 size) override; 60 void FlushRegion(CacheAddr addr, u64 size) override;
61 void InvalidateRegion(VAddr addr, u64 size) override; 61 void InvalidateRegion(CacheAddr addr, u64 size) override;
62 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 62 void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
63 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, 63 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
64 const Tegra::Engines::Fermi2D::Regs::Surface& dst, 64 const Tegra::Engines::Fermi2D::Regs::Surface& dst,
65 const Common::Rectangle<u32>& src_rect, 65 const Common::Rectangle<u32>& src_rect,
@@ -67,7 +67,7 @@ public:
67 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 67 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
68 u32 pixel_stride) override; 68 u32 pixel_stride) override;
69 bool AccelerateDrawBatch(bool is_indexed) override; 69 bool AccelerateDrawBatch(bool is_indexed) override;
70 void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) override; 70 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
71 void LoadDiskResources(const std::atomic_bool& stop_loading, 71 void LoadDiskResources(const std::atomic_bool& stop_loading,
72 const VideoCore::DiskResourceLoadCallback& callback) override; 72 const VideoCore::DiskResourceLoadCallback& callback) override;
73 73
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index e9eb6e921..57329cd61 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -57,10 +57,9 @@ static void ApplyTextureDefaults(GLuint texture, u32 max_mip_level) {
57 57
58void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { 58void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
59 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; 59 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
60 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
61 60
62 addr = cpu_addr ? *cpu_addr : 0;
63 gpu_addr = gpu_addr_; 61 gpu_addr = gpu_addr_;
62 host_ptr = memory_manager.GetPointer(gpu_addr_);
64 size_in_bytes = SizeInBytesRaw(); 63 size_in_bytes = SizeInBytesRaw();
65 64
66 if (IsPixelFormatASTC(pixel_format)) { 65 if (IsPixelFormatASTC(pixel_format)) {
@@ -446,7 +445,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
446 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), 445 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
447 params.MipBlockHeight(mip_level), params.MipHeight(mip_level), 446 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
448 params.MipBlockDepth(mip_level), 1, params.tile_width_spacing, 447 params.MipBlockDepth(mip_level), 1, params.tile_width_spacing,
449 gl_buffer.data() + offset_gl, gl_size, params.addr + offset); 448 gl_buffer.data() + offset_gl, params.host_ptr + offset);
450 offset += layer_size; 449 offset += layer_size;
451 offset_gl += gl_size; 450 offset_gl += gl_size;
452 } 451 }
@@ -455,7 +454,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
455 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), 454 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
456 params.MipBlockHeight(mip_level), params.MipHeight(mip_level), 455 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
457 params.MipBlockDepth(mip_level), depth, params.tile_width_spacing, 456 params.MipBlockDepth(mip_level), depth, params.tile_width_spacing,
458 gl_buffer.data(), gl_buffer.size(), params.addr + offset); 457 gl_buffer.data(), params.host_ptr + offset);
459 } 458 }
460} 459}
461 460
@@ -513,9 +512,9 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
513 "reinterpretation but the texture is tiled."); 512 "reinterpretation but the texture is tiled.");
514 } 513 }
515 const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes; 514 const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
516 515 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
517 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size, 516 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
518 Memory::GetPointer(dst_params.addr + src_params.size_in_bytes)); 517 memory_manager.GetPointer(dst_params.gpu_addr + src_params.size_in_bytes));
519 } 518 }
520 519
521 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 520 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -563,8 +562,8 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
563} 562}
564 563
565CachedSurface::CachedSurface(const SurfaceParams& params) 564CachedSurface::CachedSurface(const SurfaceParams& params)
566 : params(params), gl_target(SurfaceTargetToGL(params.target)), 565 : params{params}, gl_target{SurfaceTargetToGL(params.target)},
567 cached_size_in_bytes(params.size_in_bytes) { 566 cached_size_in_bytes{params.size_in_bytes}, RasterizerCacheObject{params.host_ptr} {
568 texture.Create(gl_target); 567 texture.Create(gl_target);
569 568
570 // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0) 569 // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0)
@@ -603,7 +602,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
603 602
604 ApplyTextureDefaults(texture.handle, params.max_mip_level); 603 ApplyTextureDefaults(texture.handle, params.max_mip_level);
605 604
606 OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, params.IdentityString()); 605 OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.gpu_addr, params.IdentityString());
607 606
608 // Clamp size to mapped GPU memory region 607 // 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 608 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
@@ -616,6 +615,8 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
616 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size); 615 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
617 cached_size_in_bytes = max_size; 616 cached_size_in_bytes = max_size;
618 } 617 }
618
619 cpu_addr = *memory_manager.GpuToCpuAddress(params.gpu_addr);
619} 620}
620 621
621MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64)); 622MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64));
@@ -633,10 +634,9 @@ void CachedSurface::LoadGLBuffer() {
633 const u32 bpp = params.GetFormatBpp() / 8; 634 const u32 bpp = params.GetFormatBpp() / 8;
634 const u32 copy_size = params.width * bpp; 635 const u32 copy_size = params.width * bpp;
635 if (params.pitch == copy_size) { 636 if (params.pitch == copy_size) {
636 std::memcpy(gl_buffer[0].data(), Memory::GetPointer(params.addr), 637 std::memcpy(gl_buffer[0].data(), params.host_ptr, params.size_in_bytes_gl);
637 params.size_in_bytes_gl);
638 } else { 638 } else {
639 const u8* start = Memory::GetPointer(params.addr); 639 const u8* start{params.host_ptr};
640 u8* write_to = gl_buffer[0].data(); 640 u8* write_to = gl_buffer[0].data();
641 for (u32 h = params.height; h > 0; h--) { 641 for (u32 h = params.height; h > 0; h--) {
642 std::memcpy(write_to, start, copy_size); 642 std::memcpy(write_to, start, copy_size);
@@ -680,8 +680,6 @@ void CachedSurface::FlushGLBuffer() {
680 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 680 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
681 Tegra::Texture::ConvertFromHostToGuest(gl_buffer[0].data(), params.pixel_format, params.width, 681 Tegra::Texture::ConvertFromHostToGuest(gl_buffer[0].data(), params.pixel_format, params.width,
682 params.height, params.depth, true, true); 682 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) { 683 if (params.is_tiled) {
686 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 684 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
687 params.block_width, static_cast<u32>(params.target)); 685 params.block_width, static_cast<u32>(params.target));
@@ -691,9 +689,9 @@ void CachedSurface::FlushGLBuffer() {
691 const u32 bpp = params.GetFormatBpp() / 8; 689 const u32 bpp = params.GetFormatBpp() / 8;
692 const u32 copy_size = params.width * bpp; 690 const u32 copy_size = params.width * bpp;
693 if (params.pitch == copy_size) { 691 if (params.pitch == copy_size) {
694 std::memcpy(Memory::GetPointer(params.addr), gl_buffer[0].data(), GetSizeInBytes()); 692 std::memcpy(params.host_ptr, gl_buffer[0].data(), GetSizeInBytes());
695 } else { 693 } else {
696 u8* start = Memory::GetPointer(params.addr); 694 u8* start{params.host_ptr};
697 const u8* read_to = gl_buffer[0].data(); 695 const u8* read_to = gl_buffer[0].data();
698 for (u32 h = params.height; h > 0; h--) { 696 for (u32 h = params.height; h > 0; h--) {
699 std::memcpy(start, read_to, copy_size); 697 std::memcpy(start, read_to, copy_size);
@@ -927,12 +925,12 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
927} 925}
928 926
929Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { 927Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
930 if (params.addr == 0 || params.height * params.width == 0) { 928 if (params.gpu_addr == 0 || params.height * params.width == 0) {
931 return {}; 929 return {};
932 } 930 }
933 931
934 // Look up surface in the cache based on address 932 // Look up surface in the cache based on address
935 Surface surface{TryGet(params.addr)}; 933 Surface surface{TryGet(params.host_ptr)};
936 if (surface) { 934 if (surface) {
937 if (surface->GetSurfaceParams().IsCompatibleSurface(params)) { 935 if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
938 // Use the cached surface as-is unless it's not synced with memory 936 // Use the cached surface as-is unless it's not synced with memory
@@ -981,14 +979,16 @@ void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
981 const Surface& dst_surface) { 979 const Surface& dst_surface) {
982 const auto& init_params{src_surface->GetSurfaceParams()}; 980 const auto& init_params{src_surface->GetSurfaceParams()};
983 const auto& dst_params{dst_surface->GetSurfaceParams()}; 981 const auto& dst_params{dst_surface->GetSurfaceParams()};
984 VAddr address = init_params.addr; 982 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
985 const std::size_t layer_size = dst_params.LayerMemorySize(); 983 Tegra::GPUVAddr address{init_params.gpu_addr};
984 const std::size_t layer_size{dst_params.LayerMemorySize()};
986 for (u32 layer = 0; layer < dst_params.depth; layer++) { 985 for (u32 layer = 0; layer < dst_params.depth; layer++) {
987 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) { 986 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
988 const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap); 987 const Tegra::GPUVAddr sub_address{address + dst_params.GetMipmapLevelOffset(mipmap)};
989 const Surface& copy = TryGet(sub_address); 988 const Surface& copy{TryGet(memory_manager.GetPointer(sub_address))};
990 if (!copy) 989 if (!copy) {
991 continue; 990 continue;
991 }
992 const auto& src_params{copy->GetSurfaceParams()}; 992 const auto& src_params{copy->GetSurfaceParams()};
993 const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))}; 993 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))}; 994 const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))};
@@ -1163,7 +1163,8 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1163 const auto& dst_params{dst_surface->GetSurfaceParams()}; 1163 const auto& dst_params{dst_surface->GetSurfaceParams()};
1164 1164
1165 // Flush enough memory for both the source and destination surface 1165 // Flush enough memory for both the source and destination surface
1166 FlushRegion(src_params.addr, std::max(src_params.MemorySize(), dst_params.MemorySize())); 1166 FlushRegion(ToCacheAddr(src_params.host_ptr),
1167 std::max(src_params.MemorySize(), dst_params.MemorySize()));
1167 1168
1168 LoadSurface(dst_surface); 1169 LoadSurface(dst_surface);
1169} 1170}
@@ -1215,8 +1216,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1215 return new_surface; 1216 return new_surface;
1216} 1217}
1217 1218
1218Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { 1219Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(const u8* host_ptr) const {
1219 return TryGet(addr); 1220 return TryGet(host_ptr);
1220} 1221}
1221 1222
1222void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) { 1223void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) {
@@ -1243,9 +1244,10 @@ static std::optional<u32> TryFindBestMipMap(std::size_t memory, const SurfacePar
1243 return {}; 1244 return {};
1244} 1245}
1245 1246
1246static std::optional<u32> TryFindBestLayer(VAddr addr, const SurfaceParams params, u32 mipmap) { 1247static std::optional<u32> TryFindBestLayer(Tegra::GPUVAddr addr, const SurfaceParams params,
1247 const std::size_t size = params.LayerMemorySize(); 1248 u32 mipmap) {
1248 VAddr start = params.addr + params.GetMipmapLevelOffset(mipmap); 1249 const std::size_t size{params.LayerMemorySize()};
1250 Tegra::GPUVAddr start{params.gpu_addr + params.GetMipmapLevelOffset(mipmap)};
1249 for (u32 i = 0; i < params.depth; i++) { 1251 for (u32 i = 0; i < params.depth; i++) {
1250 if (start == addr) { 1252 if (start == addr) {
1251 return {i}; 1253 return {i};
@@ -1267,7 +1269,7 @@ static bool LayerFitReinterpretSurface(RasterizerCacheOpenGL& cache, const Surfa
1267 src_params.height == dst_params.MipHeight(*level) && 1269 src_params.height == dst_params.MipHeight(*level) &&
1268 src_params.block_height >= dst_params.MipBlockHeight(*level)) { 1270 src_params.block_height >= dst_params.MipBlockHeight(*level)) {
1269 const std::optional<u32> slot = 1271 const std::optional<u32> slot =
1270 TryFindBestLayer(render_surface->GetAddr(), dst_params, *level); 1272 TryFindBestLayer(render_surface->GetSurfaceParams().gpu_addr, dst_params, *level);
1271 if (slot.has_value()) { 1273 if (slot.has_value()) {
1272 glCopyImageSubData(render_surface->Texture().handle, 1274 glCopyImageSubData(render_surface->Texture().handle,
1273 SurfaceTargetToGL(src_params.target), 0, 0, 0, 0, 1275 SurfaceTargetToGL(src_params.target), 0, 0, 0, 0,
@@ -1283,8 +1285,8 @@ static bool LayerFitReinterpretSurface(RasterizerCacheOpenGL& cache, const Surfa
1283} 1285}
1284 1286
1285static bool IsReinterpretInvalid(const Surface render_surface, const Surface blitted_surface) { 1287static bool IsReinterpretInvalid(const Surface render_surface, const Surface blitted_surface) {
1286 const VAddr bound1 = blitted_surface->GetAddr() + blitted_surface->GetMemorySize(); 1288 const VAddr bound1 = blitted_surface->GetCpuAddr() + blitted_surface->GetMemorySize();
1287 const VAddr bound2 = render_surface->GetAddr() + render_surface->GetMemorySize(); 1289 const VAddr bound2 = render_surface->GetCpuAddr() + render_surface->GetMemorySize();
1288 if (bound2 > bound1) 1290 if (bound2 > bound1)
1289 return true; 1291 return true;
1290 const auto& dst_params = blitted_surface->GetSurfaceParams(); 1292 const auto& dst_params = blitted_surface->GetSurfaceParams();
@@ -1327,7 +1329,8 @@ void RasterizerCacheOpenGL::SignalPreDrawCall() {
1327void RasterizerCacheOpenGL::SignalPostDrawCall() { 1329void RasterizerCacheOpenGL::SignalPostDrawCall() {
1328 for (u32 i = 0; i < Maxwell::NumRenderTargets; i++) { 1330 for (u32 i = 0; i < Maxwell::NumRenderTargets; i++) {
1329 if (current_color_buffers[i] != nullptr) { 1331 if (current_color_buffers[i] != nullptr) {
1330 Surface intersect = CollideOnReinterpretedSurface(current_color_buffers[i]->GetAddr()); 1332 Surface intersect =
1333 CollideOnReinterpretedSurface(current_color_buffers[i]->GetCacheAddr());
1331 if (intersect != nullptr) { 1334 if (intersect != nullptr) {
1332 PartialReinterpretSurface(current_color_buffers[i], intersect); 1335 PartialReinterpretSurface(current_color_buffers[i], intersect);
1333 texception = true; 1336 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..9366f47f2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -296,7 +296,7 @@ struct SurfaceParams {
296 bool is_array; 296 bool is_array;
297 bool srgb_conversion; 297 bool srgb_conversion;
298 // Parameters used for caching 298 // Parameters used for caching
299 VAddr addr; 299 u8* host_ptr;
300 Tegra::GPUVAddr gpu_addr; 300 Tegra::GPUVAddr gpu_addr;
301 std::size_t size_in_bytes; 301 std::size_t size_in_bytes;
302 std::size_t size_in_bytes_gl; 302 std::size_t size_in_bytes_gl;
@@ -345,10 +345,10 @@ class RasterizerOpenGL;
345 345
346class CachedSurface final : public RasterizerCacheObject { 346class CachedSurface final : public RasterizerCacheObject {
347public: 347public:
348 CachedSurface(const SurfaceParams& params); 348 explicit CachedSurface(const SurfaceParams& params);
349 349
350 VAddr GetAddr() const override { 350 VAddr GetCpuAddr() const override {
351 return params.addr; 351 return cpu_addr;
352 } 352 }
353 353
354 std::size_t GetSizeInBytes() const override { 354 std::size_t GetSizeInBytes() const override {
@@ -432,6 +432,7 @@ private:
432 std::size_t memory_size; 432 std::size_t memory_size;
433 bool reinterpreted = false; 433 bool reinterpreted = false;
434 bool must_reload = false; 434 bool must_reload = false;
435 VAddr cpu_addr{};
435}; 436};
436 437
437class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 438class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -449,7 +450,7 @@ public:
449 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); 450 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents);
450 451
451 /// Tries to find a framebuffer using on the provided CPU address 452 /// Tries to find a framebuffer using on the provided CPU address
452 Surface TryFindFramebufferSurface(VAddr addr) const; 453 Surface TryFindFramebufferSurface(const u8* host_ptr) const;
453 454
454 /// Copies the contents of one surface to another 455 /// Copies the contents of one surface to another
455 void FermiCopySurface(const Tegra::Engines::Fermi2D::Regs::Surface& src_config, 456 void FermiCopySurface(const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
@@ -506,12 +507,12 @@ private:
506 std::array<Surface, Maxwell::NumRenderTargets> current_color_buffers; 507 std::array<Surface, Maxwell::NumRenderTargets> current_color_buffers;
507 Surface last_depth_buffer; 508 Surface last_depth_buffer;
508 509
509 using SurfaceIntervalCache = boost::icl::interval_map<VAddr, Surface>; 510 using SurfaceIntervalCache = boost::icl::interval_map<CacheAddr, Surface>;
510 using SurfaceInterval = typename SurfaceIntervalCache::interval_type; 511 using SurfaceInterval = typename SurfaceIntervalCache::interval_type;
511 512
512 static auto GetReinterpretInterval(const Surface& object) { 513 static auto GetReinterpretInterval(const Surface& object) {
513 return SurfaceInterval::right_open(object->GetAddr() + 1, 514 return SurfaceInterval::right_open(object->GetCacheAddr() + 1,
514 object->GetAddr() + object->GetMemorySize() - 1); 515 object->GetCacheAddr() + object->GetMemorySize() - 1);
515 } 516 }
516 517
517 // Reinterpreted surfaces are very fragil as the game may keep rendering into them. 518 // Reinterpreted surfaces are very fragil as the game may keep rendering into them.
@@ -523,7 +524,7 @@ private:
523 reinterpret_surface->MarkReinterpreted(); 524 reinterpret_surface->MarkReinterpreted();
524 } 525 }
525 526
526 Surface CollideOnReinterpretedSurface(VAddr addr) const { 527 Surface CollideOnReinterpretedSurface(CacheAddr addr) const {
527 const SurfaceInterval interval{addr}; 528 const SurfaceInterval interval{addr};
528 for (auto& pair : 529 for (auto& pair :
529 boost::make_iterator_range(reinterpreted_surfaces.equal_range(interval))) { 530 boost::make_iterator_range(reinterpreted_surfaces.equal_range(interval))) {
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 4883e4f62..1ed740877 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -32,19 +32,16 @@ struct UnspecializedShader {
32namespace { 32namespace {
33 33
34/// Gets the address for the specified shader stage program 34/// Gets the address for the specified shader stage program
35VAddr GetShaderAddress(Maxwell::ShaderProgram program) { 35Tegra::GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) {
36 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 36 const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()};
37 const auto& shader_config = gpu.regs.shader_config[static_cast<std::size_t>(program)]; 37 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() + 38 return gpu.regs.code_address.CodeAddress() + shader_config.offset;
39 shader_config.offset);
40 ASSERT_MSG(address, "Invalid GPU address");
41 return *address;
42} 39}
43 40
44/// Gets the shader program code from memory for the specified address 41/// Gets the shader program code from memory for the specified address
45ProgramCode GetShaderCode(VAddr addr) { 42ProgramCode GetShaderCode(const u8* host_ptr) {
46 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); 43 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
47 Memory::ReadBlock(addr, program_code.data(), program_code.size() * sizeof(u64)); 44 std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64));
48 return program_code; 45 return program_code;
49} 46}
50 47
@@ -214,12 +211,13 @@ std::set<GLenum> GetSupportedFormats() {
214 211
215} // namespace 212} // namespace
216 213
217CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 214CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
218 ShaderDiskCacheOpenGL& disk_cache, 215 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
219 const PrecompiledPrograms& precompiled_programs, 216 const PrecompiledPrograms& precompiled_programs,
220 ProgramCode&& program_code, ProgramCode&& program_code_b) 217 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr)
221 : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, 218 : host_ptr{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier},
222 disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { 219 program_type{program_type}, disk_cache{disk_cache},
220 precompiled_programs{precompiled_programs}, RasterizerCacheObject{host_ptr} {
223 221
224 const std::size_t code_size = CalculateProgramSize(program_code); 222 const std::size_t code_size = CalculateProgramSize(program_code);
225 const std::size_t code_size_b = 223 const std::size_t code_size_b =
@@ -243,12 +241,13 @@ CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderPro
243 disk_cache.SaveRaw(raw); 241 disk_cache.SaveRaw(raw);
244} 242}
245 243
246CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 244CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
247 ShaderDiskCacheOpenGL& disk_cache, 245 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
248 const PrecompiledPrograms& precompiled_programs, 246 const PrecompiledPrograms& precompiled_programs,
249 GLShader::ProgramResult result) 247 GLShader::ProgramResult result, u8* host_ptr)
250 : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, 248 : cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, program_type{program_type},
251 disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { 249 disk_cache{disk_cache}, precompiled_programs{precompiled_programs}, RasterizerCacheObject{
250 host_ptr} {
252 251
253 code = std::move(result.first); 252 code = std::move(result.first);
254 entries = result.second; 253 entries = result.second;
@@ -271,7 +270,7 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive
271 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); 270 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));
272 } 271 }
273 272
274 LabelGLObject(GL_PROGRAM, program->handle, addr); 273 LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
275 } 274 }
276 275
277 handle = program->handle; 276 handle = program->handle;
@@ -323,7 +322,7 @@ GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, BaseBind
323 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); 322 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));
324 } 323 }
325 324
326 LabelGLObject(GL_PROGRAM, target_program->handle, addr, debug_name); 325 LabelGLObject(GL_PROGRAM, target_program->handle, cpu_addr, debug_name);
327 326
328 return target_program->handle; 327 return target_program->handle;
329}; 328};
@@ -486,29 +485,32 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
486 return last_shaders[static_cast<u32>(program)]; 485 return last_shaders[static_cast<u32>(program)];
487 } 486 }
488 487
489 const VAddr program_addr{GetShaderAddress(program)}; 488 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
489 const Tegra::GPUVAddr program_addr{GetShaderAddress(program)};
490 490
491 // Look up shader in the cache based on address 491 // Look up shader in the cache based on address
492 Shader shader{TryGet(program_addr)}; 492 const auto& host_ptr{memory_manager.GetPointer(program_addr)};
493 Shader shader{TryGet(host_ptr)};
493 494
494 if (!shader) { 495 if (!shader) {
495 // No shader found - create a new one 496 // No shader found - create a new one
496 ProgramCode program_code = GetShaderCode(program_addr); 497 ProgramCode program_code{GetShaderCode(host_ptr)};
497 ProgramCode program_code_b; 498 ProgramCode program_code_b;
498 if (program == Maxwell::ShaderProgram::VertexA) { 499 if (program == Maxwell::ShaderProgram::VertexA) {
499 program_code_b = GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB)); 500 program_code_b = GetShaderCode(
501 memory_manager.GetPointer(GetShaderAddress(Maxwell::ShaderProgram::VertexB)));
500 } 502 }
501 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); 503 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
502 504 const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
503 const auto found = precompiled_shaders.find(unique_identifier); 505 const auto found = precompiled_shaders.find(unique_identifier);
504 if (found != precompiled_shaders.end()) { 506 if (found != precompiled_shaders.end()) {
505 shader = 507 shader =
506 std::make_shared<CachedShader>(program_addr, unique_identifier, program, disk_cache, 508 std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache,
507 precompiled_programs, found->second); 509 precompiled_programs, found->second, host_ptr);
508 } else { 510 } else {
509 shader = std::make_shared<CachedShader>( 511 shader = std::make_shared<CachedShader>(
510 program_addr, unique_identifier, program, disk_cache, precompiled_programs, 512 cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
511 std::move(program_code), std::move(program_code_b)); 513 std::move(program_code), std::move(program_code_b), host_ptr);
512 } 514 }
513 Register(shader); 515 Register(shader);
514 } 516 }
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 97eed192f..fd1c85115 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -39,18 +39,18 @@ using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>;
39 39
40class CachedShader final : public RasterizerCacheObject { 40class CachedShader final : public RasterizerCacheObject {
41public: 41public:
42 explicit CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 42 explicit CachedShader(VAddr cpu_addr, u64 unique_identifier,
43 ShaderDiskCacheOpenGL& disk_cache, 43 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
44 const PrecompiledPrograms& precompiled_programs, 44 const PrecompiledPrograms& precompiled_programs,
45 ProgramCode&& program_code, ProgramCode&& program_code_b); 45 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr);
46 46
47 explicit CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, 47 explicit CachedShader(VAddr cpu_addr, u64 unique_identifier,
48 ShaderDiskCacheOpenGL& disk_cache, 48 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
49 const PrecompiledPrograms& precompiled_programs, 49 const PrecompiledPrograms& precompiled_programs,
50 GLShader::ProgramResult result); 50 GLShader::ProgramResult result, u8* host_ptr);
51 51
52 VAddr GetAddr() const override { 52 VAddr GetCpuAddr() const override {
53 return addr; 53 return cpu_addr;
54 } 54 }
55 55
56 std::size_t GetSizeInBytes() const override { 56 std::size_t GetSizeInBytes() const override {
@@ -91,7 +91,8 @@ private:
91 91
92 ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const; 92 ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const;
93 93
94 VAddr addr{}; 94 u8* host_ptr{};
95 VAddr cpu_addr{};
95 u64 unique_identifier{}; 96 u64 unique_identifier{};
96 Maxwell::ShaderProgram program_type{}; 97 Maxwell::ShaderProgram program_type{};
97 ShaderDiskCacheOpenGL& disk_cache; 98 ShaderDiskCacheOpenGL& disk_cache;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 8b510b6ae..5e3d862c6 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -164,12 +164,13 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
164 // Reset the screen info's display texture to its own permanent texture 164 // Reset the screen info's display texture to its own permanent texture
165 screen_info.display_texture = screen_info.texture.resource.handle; 165 screen_info.display_texture = screen_info.texture.resource.handle;
166 166
167 Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes, 167 rasterizer->FlushRegion(ToCacheAddr(Memory::GetPointer(framebuffer_addr)), size_in_bytes);
168 Memory::FlushMode::Flush);
169 168
170 VideoCore::MortonCopyPixels128(framebuffer.width, framebuffer.height, bytes_per_pixel, 4, 169 constexpr u32 linear_bpp = 4;
171 Memory::GetPointer(framebuffer_addr), 170 VideoCore::MortonCopyPixels128(VideoCore::MortonSwizzleMode::MortonToLinear,
172 gl_framebuffer_data.data(), true); 171 framebuffer.width, framebuffer.height, bytes_per_pixel,
172 linear_bpp, Memory::GetPointer(framebuffer_addr),
173 gl_framebuffer_data.data());
173 174
174 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); 175 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
175 176
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 4a33a6c84..95eab3fec 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -17,6 +17,11 @@
17 17
18namespace Vulkan { 18namespace Vulkan {
19 19
20CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, u64 offset,
21 std::size_t alignment, u8* host_ptr)
22 : cpu_addr{cpu_addr}, size{size}, offset{offset}, alignment{alignment}, RasterizerCacheObject{
23 host_ptr} {}
24
20VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, 25VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager,
21 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, 26 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
22 VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size) 27 VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size)
@@ -37,16 +42,18 @@ VKBufferCache::~VKBufferCache() = default;
37u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment, 42u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment,
38 bool cache) { 43 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..8b415744b 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>> {
diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp
new file mode 100644
index 000000000..ed3178f09
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp
@@ -0,0 +1,81 @@
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 <cstring>
6#include <optional>
7#include <unordered_map>
8
9#include "common/assert.h"
10#include "common/cityhash.h"
11#include "video_core/renderer_vulkan/declarations.h"
12#include "video_core/renderer_vulkan/maxwell_to_vk.h"
13#include "video_core/renderer_vulkan/vk_sampler_cache.h"
14#include "video_core/textures/texture.h"
15
16namespace Vulkan {
17
18static std::optional<vk::BorderColor> TryConvertBorderColor(std::array<float, 4> color) {
19 // TODO(Rodrigo): Manage integer border colors
20 if (color == std::array<float, 4>{0, 0, 0, 0}) {
21 return vk::BorderColor::eFloatTransparentBlack;
22 } else if (color == std::array<float, 4>{0, 0, 0, 1}) {
23 return vk::BorderColor::eFloatOpaqueBlack;
24 } else if (color == std::array<float, 4>{1, 1, 1, 1}) {
25 return vk::BorderColor::eFloatOpaqueWhite;
26 } else {
27 return {};
28 }
29}
30
31std::size_t SamplerCacheKey::Hash() const {
32 static_assert(sizeof(raw) % sizeof(u64) == 0);
33 return static_cast<std::size_t>(
34 Common::CityHash64(reinterpret_cast<const char*>(raw.data()), sizeof(raw) / sizeof(u64)));
35}
36
37bool SamplerCacheKey::operator==(const SamplerCacheKey& rhs) const {
38 return raw == rhs.raw;
39}
40
41VKSamplerCache::VKSamplerCache(const VKDevice& device) : device{device} {}
42
43VKSamplerCache::~VKSamplerCache() = default;
44
45vk::Sampler VKSamplerCache::GetSampler(const Tegra::Texture::TSCEntry& tsc) {
46 const auto [entry, is_cache_miss] = cache.try_emplace(SamplerCacheKey{tsc});
47 auto& sampler = entry->second;
48 if (is_cache_miss) {
49 sampler = CreateSampler(tsc);
50 }
51 return *sampler;
52}
53
54UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) {
55 const float max_anisotropy = tsc.GetMaxAnisotropy();
56 const bool has_anisotropy = max_anisotropy > 1.0f;
57
58 const auto border_color = tsc.GetBorderColor();
59 const auto vk_border_color = TryConvertBorderColor(border_color);
60 UNIMPLEMENTED_IF_MSG(!vk_border_color, "Unimplemented border color {} {} {} {}",
61 border_color[0], border_color[1], border_color[2], border_color[3]);
62
63 constexpr bool unnormalized_coords = false;
64
65 const vk::SamplerCreateInfo sampler_ci(
66 {}, MaxwellToVK::Sampler::Filter(tsc.mag_filter),
67 MaxwellToVK::Sampler::Filter(tsc.min_filter),
68 MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
69 MaxwellToVK::Sampler::WrapMode(tsc.wrap_u), MaxwellToVK::Sampler::WrapMode(tsc.wrap_v),
70 MaxwellToVK::Sampler::WrapMode(tsc.wrap_p), tsc.GetLodBias(), has_anisotropy,
71 max_anisotropy, tsc.depth_compare_enabled,
72 MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), tsc.GetMinLod(),
73 tsc.GetMaxLod(), vk_border_color.value_or(vk::BorderColor::eFloatTransparentBlack),
74 unnormalized_coords);
75
76 const auto& dld = device.GetDispatchLoader();
77 const auto dev = device.GetLogical();
78 return dev.createSamplerUnique(sampler_ci, nullptr, dld);
79}
80
81} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.h b/src/video_core/renderer_vulkan/vk_sampler_cache.h
new file mode 100644
index 000000000..c6394dc87
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_sampler_cache.h
@@ -0,0 +1,56 @@
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 <unordered_map>
8
9#include "common/common_types.h"
10#include "video_core/renderer_vulkan/declarations.h"
11#include "video_core/textures/texture.h"
12
13namespace Vulkan {
14
15class VKDevice;
16
17struct SamplerCacheKey final : public Tegra::Texture::TSCEntry {
18 std::size_t Hash() const;
19
20 bool operator==(const SamplerCacheKey& rhs) const;
21
22 bool operator!=(const SamplerCacheKey& rhs) const {
23 return !operator==(rhs);
24 }
25};
26
27} // namespace Vulkan
28
29namespace std {
30
31template <>
32struct hash<Vulkan::SamplerCacheKey> {
33 std::size_t operator()(const Vulkan::SamplerCacheKey& k) const noexcept {
34 return k.Hash();
35 }
36};
37
38} // namespace std
39
40namespace Vulkan {
41
42class VKSamplerCache {
43public:
44 explicit VKSamplerCache(const VKDevice& device);
45 ~VKSamplerCache();
46
47 vk::Sampler GetSampler(const Tegra::Texture::TSCEntry& tsc);
48
49private:
50 UniqueSampler CreateSampler(const Tegra::Texture::TSCEntry& tsc);
51
52 const VKDevice& device;
53 std::unordered_map<SamplerCacheKey, UniqueSampler> cache;
54};
55
56} // 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/video_core/textures/texture.h b/src/video_core/textures/texture.h
index b8675f702..93ecc6e31 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -283,31 +283,36 @@ enum class TextureMipmapFilter : u32 {
283 283
284struct TSCEntry { 284struct TSCEntry {
285 union { 285 union {
286 BitField<0, 3, WrapMode> wrap_u; 286 struct {
287 BitField<3, 3, WrapMode> wrap_v; 287 union {
288 BitField<6, 3, WrapMode> wrap_p; 288 BitField<0, 3, WrapMode> wrap_u;
289 BitField<9, 1, u32> depth_compare_enabled; 289 BitField<3, 3, WrapMode> wrap_v;
290 BitField<10, 3, DepthCompareFunc> depth_compare_func; 290 BitField<6, 3, WrapMode> wrap_p;
291 BitField<13, 1, u32> srgb_conversion; 291 BitField<9, 1, u32> depth_compare_enabled;
292 BitField<20, 3, u32> max_anisotropy; 292 BitField<10, 3, DepthCompareFunc> depth_compare_func;
293 BitField<13, 1, u32> srgb_conversion;
294 BitField<20, 3, u32> max_anisotropy;
295 };
296 union {
297 BitField<0, 2, TextureFilter> mag_filter;
298 BitField<4, 2, TextureFilter> min_filter;
299 BitField<6, 2, TextureMipmapFilter> mipmap_filter;
300 BitField<9, 1, u32> cubemap_interface_filtering;
301 BitField<12, 13, u32> mip_lod_bias;
302 };
303 union {
304 BitField<0, 12, u32> min_lod_clamp;
305 BitField<12, 12, u32> max_lod_clamp;
306 BitField<24, 8, u32> srgb_border_color_r;
307 };
308 union {
309 BitField<12, 8, u32> srgb_border_color_g;
310 BitField<20, 8, u32> srgb_border_color_b;
311 };
312 std::array<f32, 4> border_color;
313 };
314 std::array<u8, 0x20> raw;
293 }; 315 };
294 union {
295 BitField<0, 2, TextureFilter> mag_filter;
296 BitField<4, 2, TextureFilter> min_filter;
297 BitField<6, 2, TextureMipmapFilter> mipmap_filter;
298 BitField<9, 1, u32> cubemap_interface_filtering;
299 BitField<12, 13, u32> mip_lod_bias;
300 };
301 union {
302 BitField<0, 12, u32> min_lod_clamp;
303 BitField<12, 12, u32> max_lod_clamp;
304 BitField<24, 8, u32> srgb_border_color_r;
305 };
306 union {
307 BitField<12, 8, u32> srgb_border_color_g;
308 BitField<20, 8, u32> srgb_border_color_b;
309 };
310 std::array<f32, 4> border_color;
311 316
312 float GetMaxAnisotropy() const { 317 float GetMaxAnisotropy() const {
313 return static_cast<float>(1U << max_anisotropy); 318 return static_cast<float>(1U << max_anisotropy);
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index d2c97b1f8..05ad19e1d 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;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 74dc6bb28..4650f96a3 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,30 @@ 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(); 416 Settings::values.enable_nfc = ReadSetting("enable_nfc", true).toBool();
420 417
421 Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0, 418 Settings::values.current_user =
422 Service::Account::MAX_USERS - 1); 419 std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1);
423 420
424 Settings::values.language_index = qt_config->value("language_index", 1).toInt(); 421 Settings::values.language_index = ReadSetting("language_index", 1).toInt();
425 422
426 const auto rng_seed_enabled = qt_config->value("rng_seed_enabled", false).toBool(); 423 const auto rng_seed_enabled = ReadSetting("rng_seed_enabled", false).toBool();
427 if (rng_seed_enabled) { 424 if (rng_seed_enabled) {
428 Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong(); 425 Settings::values.rng_seed = ReadSetting("rng_seed", 0).toULongLong();
429 } else { 426 } else {
430 Settings::values.rng_seed = std::nullopt; 427 Settings::values.rng_seed = std::nullopt;
431 } 428 }
432 429
433 const auto custom_rtc_enabled = qt_config->value("custom_rtc_enabled", false).toBool(); 430 const auto custom_rtc_enabled = ReadSetting("custom_rtc_enabled", false).toBool();
434 if (custom_rtc_enabled) { 431 if (custom_rtc_enabled) {
435 Settings::values.custom_rtc = 432 Settings::values.custom_rtc =
436 std::chrono::seconds(qt_config->value("custom_rtc", 0).toULongLong()); 433 std::chrono::seconds(ReadSetting("custom_rtc", 0).toULongLong());
437 } else { 434 } else {
438 Settings::values.custom_rtc = std::nullopt; 435 Settings::values.custom_rtc = std::nullopt;
439 } 436 }
@@ -441,35 +438,35 @@ void Config::ReadValues() {
441 qt_config->endGroup(); 438 qt_config->endGroup();
442 439
443 qt_config->beginGroup("Miscellaneous"); 440 qt_config->beginGroup("Miscellaneous");
444 Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); 441 Settings::values.log_filter = ReadSetting("log_filter", "*:Info").toString().toStdString();
445 Settings::values.use_dev_keys = qt_config->value("use_dev_keys", false).toBool(); 442 Settings::values.use_dev_keys = ReadSetting("use_dev_keys", false).toBool();
446 qt_config->endGroup(); 443 qt_config->endGroup();
447 444
448 qt_config->beginGroup("Debugging"); 445 qt_config->beginGroup("Debugging");
449 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 446 Settings::values.use_gdbstub = ReadSetting("use_gdbstub", false).toBool();
450 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 447 Settings::values.gdbstub_port = ReadSetting("gdbstub_port", 24689).toInt();
451 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); 448 Settings::values.program_args = ReadSetting("program_args", "").toString().toStdString();
452 Settings::values.dump_exefs = qt_config->value("dump_exefs", false).toBool(); 449 Settings::values.dump_exefs = ReadSetting("dump_exefs", false).toBool();
453 Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); 450 Settings::values.dump_nso = ReadSetting("dump_nso", false).toBool();
454 qt_config->endGroup(); 451 qt_config->endGroup();
455 452
456 qt_config->beginGroup("WebService"); 453 qt_config->beginGroup("WebService");
457 Settings::values.enable_telemetry = qt_config->value("enable_telemetry", true).toBool(); 454 Settings::values.enable_telemetry = ReadSetting("enable_telemetry", true).toBool();
458 Settings::values.web_api_url = 455 Settings::values.web_api_url =
459 qt_config->value("web_api_url", "https://api.yuzu-emu.org").toString().toStdString(); 456 ReadSetting("web_api_url", "https://api.yuzu-emu.org").toString().toStdString();
460 Settings::values.yuzu_username = qt_config->value("yuzu_username").toString().toStdString(); 457 Settings::values.yuzu_username = ReadSetting("yuzu_username").toString().toStdString();
461 Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString(); 458 Settings::values.yuzu_token = ReadSetting("yuzu_token").toString().toStdString();
462 qt_config->endGroup(); 459 qt_config->endGroup();
463 460
464 const auto size = qt_config->beginReadArray("DisabledAddOns"); 461 const auto size = qt_config->beginReadArray("DisabledAddOns");
465 for (int i = 0; i < size; ++i) { 462 for (int i = 0; i < size; ++i) {
466 qt_config->setArrayIndex(i); 463 qt_config->setArrayIndex(i);
467 const auto title_id = qt_config->value("title_id", 0).toULongLong(); 464 const auto title_id = ReadSetting("title_id", 0).toULongLong();
468 std::vector<std::string> out; 465 std::vector<std::string> out;
469 const auto d_size = qt_config->beginReadArray("disabled"); 466 const auto d_size = qt_config->beginReadArray("disabled");
470 for (int j = 0; j < d_size; ++j) { 467 for (int j = 0; j < d_size; ++j) {
471 qt_config->setArrayIndex(j); 468 qt_config->setArrayIndex(j);
472 out.push_back(qt_config->value("d", "").toString().toStdString()); 469 out.push_back(ReadSetting("d", "").toString().toStdString());
473 } 470 }
474 qt_config->endArray(); 471 qt_config->endArray();
475 Settings::values.disabled_addons.insert_or_assign(title_id, out); 472 Settings::values.disabled_addons.insert_or_assign(title_id, out);
@@ -477,41 +474,38 @@ void Config::ReadValues() {
477 qt_config->endArray(); 474 qt_config->endArray();
478 475
479 qt_config->beginGroup("UI"); 476 qt_config->beginGroup("UI");
480 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); 477 UISettings::values.theme = ReadSetting("theme", UISettings::themes[0].second).toString();
481 UISettings::values.enable_discord_presence = 478 UISettings::values.enable_discord_presence =
482 qt_config->value("enable_discord_presence", true).toBool(); 479 ReadSetting("enable_discord_presence", true).toBool();
483 UISettings::values.screenshot_resolution_factor = 480 UISettings::values.screenshot_resolution_factor =
484 static_cast<u16>(qt_config->value("screenshot_resolution_factor", 0).toUInt()); 481 static_cast<u16>(ReadSetting("screenshot_resolution_factor", 0).toUInt());
485 UISettings::values.select_user_on_boot = 482 UISettings::values.select_user_on_boot = ReadSetting("select_user_on_boot", false).toBool();
486 qt_config->value("select_user_on_boot", false).toBool();
487 483
488 qt_config->beginGroup("UIGameList"); 484 qt_config->beginGroup("UIGameList");
489 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); 485 UISettings::values.show_unknown = ReadSetting("show_unknown", true).toBool();
490 UISettings::values.show_add_ons = qt_config->value("show_add_ons", true).toBool(); 486 UISettings::values.show_add_ons = ReadSetting("show_add_ons", true).toBool();
491 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt(); 487 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(); 488 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(); 489 UISettings::values.row_2_text_id = ReadSetting("row_2_text_id", 2).toUInt();
494 qt_config->endGroup(); 490 qt_config->endGroup();
495 491
496 qt_config->beginGroup("UILayout"); 492 qt_config->beginGroup("UILayout");
497 UISettings::values.geometry = qt_config->value("geometry").toByteArray(); 493 UISettings::values.geometry = ReadSetting("geometry").toByteArray();
498 UISettings::values.state = qt_config->value("state").toByteArray(); 494 UISettings::values.state = ReadSetting("state").toByteArray();
499 UISettings::values.renderwindow_geometry = 495 UISettings::values.renderwindow_geometry = ReadSetting("geometryRenderWindow").toByteArray();
500 qt_config->value("geometryRenderWindow").toByteArray(); 496 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 = 497 UISettings::values.microprofile_geometry =
504 qt_config->value("microProfileDialogGeometry").toByteArray(); 498 ReadSetting("microProfileDialogGeometry").toByteArray();
505 UISettings::values.microprofile_visible = 499 UISettings::values.microprofile_visible =
506 qt_config->value("microProfileDialogVisible", false).toBool(); 500 ReadSetting("microProfileDialogVisible", false).toBool();
507 qt_config->endGroup(); 501 qt_config->endGroup();
508 502
509 qt_config->beginGroup("Paths"); 503 qt_config->beginGroup("Paths");
510 UISettings::values.roms_path = qt_config->value("romsPath").toString(); 504 UISettings::values.roms_path = ReadSetting("romsPath").toString();
511 UISettings::values.symbols_path = qt_config->value("symbolsPath").toString(); 505 UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
512 UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString(); 506 UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString();
513 UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool(); 507 UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool();
514 UISettings::values.recent_files = qt_config->value("recentFiles").toStringList(); 508 UISettings::values.recent_files = ReadSetting("recentFiles").toStringList();
515 qt_config->endGroup(); 509 qt_config->endGroup();
516 510
517 qt_config->beginGroup("Shortcuts"); 511 qt_config->beginGroup("Shortcuts");
@@ -524,8 +518,8 @@ void Config::ReadValues() {
524 qt_config->beginGroup(hotkey); 518 qt_config->beginGroup(hotkey);
525 UISettings::values.shortcuts.emplace_back(UISettings::Shortcut( 519 UISettings::values.shortcuts.emplace_back(UISettings::Shortcut(
526 group + "/" + hotkey, 520 group + "/" + hotkey,
527 UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), 521 UISettings::ContextualShortcut(ReadSetting("KeySeq").toString(),
528 qt_config->value("Context").toInt()))); 522 ReadSetting("Context").toInt())));
529 qt_config->endGroup(); 523 qt_config->endGroup();
530 } 524 }
531 525
@@ -533,16 +527,16 @@ void Config::ReadValues() {
533 } 527 }
534 qt_config->endGroup(); 528 qt_config->endGroup();
535 529
536 UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); 530 UISettings::values.single_window_mode = ReadSetting("singleWindowMode", true).toBool();
537 UISettings::values.fullscreen = qt_config->value("fullscreen", false).toBool(); 531 UISettings::values.fullscreen = ReadSetting("fullscreen", false).toBool();
538 UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); 532 UISettings::values.display_titlebar = ReadSetting("displayTitleBars", true).toBool();
539 UISettings::values.show_filter_bar = qt_config->value("showFilterBar", true).toBool(); 533 UISettings::values.show_filter_bar = ReadSetting("showFilterBar", true).toBool();
540 UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool(); 534 UISettings::values.show_status_bar = ReadSetting("showStatusBar", true).toBool();
541 UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool(); 535 UISettings::values.confirm_before_closing = ReadSetting("confirmClose", true).toBool();
542 UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); 536 UISettings::values.first_start = ReadSetting("firstStart", true).toBool();
543 UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt(); 537 UISettings::values.callout_flags = ReadSetting("calloutFlags", 0).toUInt();
544 UISettings::values.show_console = qt_config->value("showConsole", false).toBool(); 538 UISettings::values.show_console = ReadSetting("showConsole", false).toBool();
545 UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt(); 539 UISettings::values.profile_index = ReadSetting("profileIndex", 0).toUInt();
546 540
547 ApplyDefaultProfileIfInputInvalid(); 541 ApplyDefaultProfileIfInputInvalid();
548 542
@@ -553,62 +547,79 @@ void Config::SavePlayerValues() {
553 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 547 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
554 const auto& player = Settings::values.players[p]; 548 const auto& player = Settings::values.players[p];
555 549
556 qt_config->setValue(QString("player_%1_connected").arg(p), player.connected); 550 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)); 551 WriteSetting(QString("player_%1_type").arg(p), static_cast<u8>(player.type),
552 static_cast<u8>(Settings::ControllerType::DualJoycon));
558 553
559 qt_config->setValue(QString("player_%1_body_color_left").arg(p), player.body_color_left); 554 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); 555 Settings::JOYCON_BODY_NEON_BLUE);
561 qt_config->setValue(QString("player_%1_button_color_left").arg(p), 556 WriteSetting(QString("player_%1_body_color_right").arg(p), player.body_color_right,
562 player.button_color_left); 557 Settings::JOYCON_BODY_NEON_RED);
563 qt_config->setValue(QString("player_%1_button_color_right").arg(p), 558 WriteSetting(QString("player_%1_button_color_left").arg(p), player.button_color_left,
564 player.button_color_right); 559 Settings::JOYCON_BUTTONS_NEON_BLUE);
560 WriteSetting(QString("player_%1_button_color_right").arg(p), player.button_color_right,
561 Settings::JOYCON_BUTTONS_NEON_RED);
565 562
566 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 563 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
567 qt_config->setValue(QString("player_%1_").arg(p) + 564 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
568 QString::fromStdString(Settings::NativeButton::mapping[i]), 565 WriteSetting(QString("player_%1_").arg(p) +
569 QString::fromStdString(player.buttons[i])); 566 QString::fromStdString(Settings::NativeButton::mapping[i]),
567 QString::fromStdString(player.buttons[i]),
568 QString::fromStdString(default_param));
570 } 569 }
571 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 570 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
572 qt_config->setValue(QString("player_%1_").arg(p) + 571 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
573 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 572 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
574 QString::fromStdString(player.analogs[i])); 573 default_analogs[i][3], default_analogs[i][4], 0.5f);
574 WriteSetting(QString("player_%1_").arg(p) +
575 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
576 QString::fromStdString(player.analogs[i]),
577 QString::fromStdString(default_param));
575 } 578 }
576 } 579 }
577} 580}
578 581
579void Config::SaveDebugValues() { 582void Config::SaveDebugValues() {
580 qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled); 583 WriteSetting("debug_pad_enabled", Settings::values.debug_pad_enabled, false);
581 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 584 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
582 qt_config->setValue(QString("debug_pad_") + 585 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
583 QString::fromStdString(Settings::NativeButton::mapping[i]), 586 WriteSetting(QString("debug_pad_") +
584 QString::fromStdString(Settings::values.debug_pad_buttons[i])); 587 QString::fromStdString(Settings::NativeButton::mapping[i]),
588 QString::fromStdString(Settings::values.debug_pad_buttons[i]),
589 QString::fromStdString(default_param));
585 } 590 }
586 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 591 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
587 qt_config->setValue(QString("debug_pad_") + 592 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
588 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 593 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
589 QString::fromStdString(Settings::values.debug_pad_analogs[i])); 594 default_analogs[i][3], default_analogs[i][4], 0.5f);
595 WriteSetting(QString("debug_pad_") +
596 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
597 QString::fromStdString(Settings::values.debug_pad_analogs[i]),
598 QString::fromStdString(default_param));
590 } 599 }
591} 600}
592 601
593void Config::SaveMouseValues() { 602void Config::SaveMouseValues() {
594 qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled); 603 WriteSetting("mouse_enabled", Settings::values.mouse_enabled, false);
595 604
596 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { 605 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
597 qt_config->setValue(QString("mouse_") + 606 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
598 QString::fromStdString(Settings::NativeMouseButton::mapping[i]), 607 WriteSetting(QString("mouse_") +
599 QString::fromStdString(Settings::values.mouse_buttons[i])); 608 QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
609 QString::fromStdString(Settings::values.mouse_buttons[i]),
610 QString::fromStdString(default_param));
600 } 611 }
601} 612}
602 613
603void Config::SaveTouchscreenValues() { 614void Config::SaveTouchscreenValues() {
604 qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled); 615 WriteSetting("touchscreen_enabled", Settings::values.touchscreen.enabled, true);
605 qt_config->setValue("touchscreen_device", 616 WriteSetting("touchscreen_device", QString::fromStdString(Settings::values.touchscreen.device),
606 QString::fromStdString(Settings::values.touchscreen.device)); 617 "engine:emu_window");
607 618
608 qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger); 619 WriteSetting("touchscreen_finger", Settings::values.touchscreen.finger, 0);
609 qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle); 620 WriteSetting("touchscreen_angle", Settings::values.touchscreen.rotation_angle, 0);
610 qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x); 621 WriteSetting("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x, 15);
611 qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y); 622 WriteSetting("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y, 15);
612} 623}
613 624
614void Config::SaveValues() { 625void Config::SaveValues() {
@@ -619,91 +630,96 @@ void Config::SaveValues() {
619 SaveMouseValues(); 630 SaveMouseValues();
620 SaveTouchscreenValues(); 631 SaveTouchscreenValues();
621 632
622 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); 633 WriteSetting("motion_device", QString::fromStdString(Settings::values.motion_device),
623 qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled); 634 "engine:motion_emu,update_period:100,sensitivity:0.01");
635 WriteSetting("keyboard_enabled", Settings::values.keyboard_enabled, false);
624 636
625 qt_config->endGroup(); 637 qt_config->endGroup();
626 638
627 qt_config->beginGroup("Core"); 639 qt_config->beginGroup("Core");
628 qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit); 640 WriteSetting("use_cpu_jit", Settings::values.use_cpu_jit, true);
629 qt_config->setValue("use_multi_core", Settings::values.use_multi_core); 641 WriteSetting("use_multi_core", Settings::values.use_multi_core, false);
630 qt_config->endGroup(); 642 qt_config->endGroup();
631 643
632 qt_config->beginGroup("Renderer"); 644 qt_config->beginGroup("Renderer");
633 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); 645 WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0);
634 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); 646 WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true);
635 qt_config->setValue("frame_limit", Settings::values.frame_limit); 647 WriteSetting("frame_limit", Settings::values.frame_limit, 100);
636 qt_config->setValue("use_disk_shader_cache", Settings::values.use_disk_shader_cache); 648 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); 649 WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false);
638 qt_config->setValue("use_asynchronous_gpu_emulation", 650 WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation,
639 Settings::values.use_asynchronous_gpu_emulation); 651 false);
640 652
641 // Cast to double because Qt's written float values are not human-readable 653 // Cast to double because Qt's written float values are not human-readable
642 qt_config->setValue("bg_red", (double)Settings::values.bg_red); 654 WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0);
643 qt_config->setValue("bg_green", (double)Settings::values.bg_green); 655 WriteSetting("bg_green", (double)Settings::values.bg_green, 0.0);
644 qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); 656 WriteSetting("bg_blue", (double)Settings::values.bg_blue, 0.0);
645 qt_config->endGroup(); 657 qt_config->endGroup();
646 658
647 qt_config->beginGroup("Audio"); 659 qt_config->beginGroup("Audio");
648 qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); 660 WriteSetting("output_engine", QString::fromStdString(Settings::values.sink_id), "auto");
649 qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching); 661 WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true);
650 qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id)); 662 WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto");
651 qt_config->setValue("volume", Settings::values.volume); 663 WriteSetting("volume", Settings::values.volume, 1.0f);
652 qt_config->endGroup(); 664 qt_config->endGroup();
653 665
654 qt_config->beginGroup("Data Storage"); 666 qt_config->beginGroup("Data Storage");
655 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); 667 WriteSetting("use_virtual_sd", Settings::values.use_virtual_sd, true);
656 qt_config->setValue("nand_directory", 668 WriteSetting("nand_directory",
657 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 669 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
658 qt_config->setValue("sdmc_directory", 670 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
659 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 671 WriteSetting("sdmc_directory",
672 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
673 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
660 qt_config->endGroup(); 674 qt_config->endGroup();
661 675
662 qt_config->beginGroup("System"); 676 qt_config->beginGroup("System");
663 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 677 WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false);
664 qt_config->setValue("enable_nfc", Settings::values.enable_nfc); 678 WriteSetting("enable_nfc", Settings::values.enable_nfc, true);
665 qt_config->setValue("current_user", Settings::values.current_user); 679 WriteSetting("current_user", Settings::values.current_user, 0);
666 qt_config->setValue("language_index", Settings::values.language_index); 680 WriteSetting("language_index", Settings::values.language_index, 1);
667 681
668 qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value()); 682 WriteSetting("rng_seed_enabled", Settings::values.rng_seed.has_value(), false);
669 qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0)); 683 WriteSetting("rng_seed", Settings::values.rng_seed.value_or(0), 0);
670 684
671 qt_config->setValue("custom_rtc_enabled", Settings::values.custom_rtc.has_value()); 685 WriteSetting("custom_rtc_enabled", Settings::values.custom_rtc.has_value(), false);
672 qt_config->setValue("custom_rtc", 686 WriteSetting("custom_rtc",
673 QVariant::fromValue<long long>( 687 QVariant::fromValue<long long>(
674 Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count())); 688 Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
689 0);
675 690
676 qt_config->endGroup(); 691 qt_config->endGroup();
677 692
678 qt_config->beginGroup("Miscellaneous"); 693 qt_config->beginGroup("Miscellaneous");
679 qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); 694 WriteSetting("log_filter", QString::fromStdString(Settings::values.log_filter), "*:Info");
680 qt_config->setValue("use_dev_keys", Settings::values.use_dev_keys); 695 WriteSetting("use_dev_keys", Settings::values.use_dev_keys, false);
681 qt_config->endGroup(); 696 qt_config->endGroup();
682 697
683 qt_config->beginGroup("Debugging"); 698 qt_config->beginGroup("Debugging");
684 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 699 WriteSetting("use_gdbstub", Settings::values.use_gdbstub, false);
685 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 700 WriteSetting("gdbstub_port", Settings::values.gdbstub_port, 24689);
686 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); 701 WriteSetting("program_args", QString::fromStdString(Settings::values.program_args), "");
687 qt_config->setValue("dump_exefs", Settings::values.dump_exefs); 702 WriteSetting("dump_exefs", Settings::values.dump_exefs, false);
688 qt_config->setValue("dump_nso", Settings::values.dump_nso); 703 WriteSetting("dump_nso", Settings::values.dump_nso, false);
689 qt_config->endGroup(); 704 qt_config->endGroup();
690 705
691 qt_config->beginGroup("WebService"); 706 qt_config->beginGroup("WebService");
692 qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry); 707 WriteSetting("enable_telemetry", Settings::values.enable_telemetry, true);
693 qt_config->setValue("web_api_url", QString::fromStdString(Settings::values.web_api_url)); 708 WriteSetting("web_api_url", QString::fromStdString(Settings::values.web_api_url),
694 qt_config->setValue("yuzu_username", QString::fromStdString(Settings::values.yuzu_username)); 709 "https://api.yuzu-emu.org");
695 qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); 710 WriteSetting("yuzu_username", QString::fromStdString(Settings::values.yuzu_username));
711 WriteSetting("yuzu_token", QString::fromStdString(Settings::values.yuzu_token));
696 qt_config->endGroup(); 712 qt_config->endGroup();
697 713
698 qt_config->beginWriteArray("DisabledAddOns"); 714 qt_config->beginWriteArray("DisabledAddOns");
699 int i = 0; 715 int i = 0;
700 for (const auto& elem : Settings::values.disabled_addons) { 716 for (const auto& elem : Settings::values.disabled_addons) {
701 qt_config->setArrayIndex(i); 717 qt_config->setArrayIndex(i);
702 qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first)); 718 WriteSetting("title_id", QVariant::fromValue<u64>(elem.first), 0);
703 qt_config->beginWriteArray("disabled"); 719 qt_config->beginWriteArray("disabled");
704 for (std::size_t j = 0; j < elem.second.size(); ++j) { 720 for (std::size_t j = 0; j < elem.second.size(); ++j) {
705 qt_config->setArrayIndex(static_cast<int>(j)); 721 qt_config->setArrayIndex(static_cast<int>(j));
706 qt_config->setValue("d", QString::fromStdString(elem.second[j])); 722 WriteSetting("d", QString::fromStdString(elem.second[j]), "");
707 } 723 }
708 qt_config->endArray(); 724 qt_config->endArray();
709 ++i; 725 ++i;
@@ -711,60 +727,86 @@ void Config::SaveValues() {
711 qt_config->endArray(); 727 qt_config->endArray();
712 728
713 qt_config->beginGroup("UI"); 729 qt_config->beginGroup("UI");
714 qt_config->setValue("theme", UISettings::values.theme); 730 WriteSetting("theme", UISettings::values.theme, UISettings::themes[0].second);
715 qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); 731 WriteSetting("enable_discord_presence", UISettings::values.enable_discord_presence, true);
716 qt_config->setValue("screenshot_resolution_factor", 732 WriteSetting("screenshot_resolution_factor", UISettings::values.screenshot_resolution_factor,
717 UISettings::values.screenshot_resolution_factor); 733 0);
718 qt_config->setValue("select_user_on_boot", UISettings::values.select_user_on_boot); 734 WriteSetting("select_user_on_boot", UISettings::values.select_user_on_boot, false);
719 735
720 qt_config->beginGroup("UIGameList"); 736 qt_config->beginGroup("UIGameList");
721 qt_config->setValue("show_unknown", UISettings::values.show_unknown); 737 WriteSetting("show_unknown", UISettings::values.show_unknown, true);
722 qt_config->setValue("show_add_ons", UISettings::values.show_add_ons); 738 WriteSetting("show_add_ons", UISettings::values.show_add_ons, true);
723 qt_config->setValue("icon_size", UISettings::values.icon_size); 739 WriteSetting("icon_size", UISettings::values.icon_size, 64);
724 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id); 740 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); 741 WriteSetting("row_2_text_id", UISettings::values.row_2_text_id, 2);
726 qt_config->endGroup(); 742 qt_config->endGroup();
727 743
728 qt_config->beginGroup("UILayout"); 744 qt_config->beginGroup("UILayout");
729 qt_config->setValue("geometry", UISettings::values.geometry); 745 WriteSetting("geometry", UISettings::values.geometry);
730 qt_config->setValue("state", UISettings::values.state); 746 WriteSetting("state", UISettings::values.state);
731 qt_config->setValue("geometryRenderWindow", UISettings::values.renderwindow_geometry); 747 WriteSetting("geometryRenderWindow", UISettings::values.renderwindow_geometry);
732 qt_config->setValue("gameListHeaderState", UISettings::values.gamelist_header_state); 748 WriteSetting("gameListHeaderState", UISettings::values.gamelist_header_state);
733 qt_config->setValue("microProfileDialogGeometry", UISettings::values.microprofile_geometry); 749 WriteSetting("microProfileDialogGeometry", UISettings::values.microprofile_geometry);
734 qt_config->setValue("microProfileDialogVisible", UISettings::values.microprofile_visible); 750 WriteSetting("microProfileDialogVisible", UISettings::values.microprofile_visible, false);
735 qt_config->endGroup(); 751 qt_config->endGroup();
736 752
737 qt_config->beginGroup("Paths"); 753 qt_config->beginGroup("Paths");
738 qt_config->setValue("romsPath", UISettings::values.roms_path); 754 WriteSetting("romsPath", UISettings::values.roms_path);
739 qt_config->setValue("symbolsPath", UISettings::values.symbols_path); 755 WriteSetting("symbolsPath", UISettings::values.symbols_path);
740 qt_config->setValue("screenshotPath", UISettings::values.screenshot_path); 756 WriteSetting("screenshotPath", UISettings::values.screenshot_path);
741 qt_config->setValue("gameListRootDir", UISettings::values.gamedir); 757 WriteSetting("gameListRootDir", UISettings::values.gamedir, ".");
742 qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan); 758 WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false);
743 qt_config->setValue("recentFiles", UISettings::values.recent_files); 759 WriteSetting("recentFiles", UISettings::values.recent_files);
744 qt_config->endGroup(); 760 qt_config->endGroup();
745 761
746 qt_config->beginGroup("Shortcuts"); 762 qt_config->beginGroup("Shortcuts");
747 for (auto shortcut : UISettings::values.shortcuts) { 763 for (auto shortcut : UISettings::values.shortcuts) {
748 qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first); 764 WriteSetting(shortcut.first + "/KeySeq", shortcut.second.first);
749 qt_config->setValue(shortcut.first + "/Context", shortcut.second.second); 765 WriteSetting(shortcut.first + "/Context", shortcut.second.second);
750 } 766 }
751 qt_config->endGroup(); 767 qt_config->endGroup();
752 768
753 qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode); 769 WriteSetting("singleWindowMode", UISettings::values.single_window_mode, true);
754 qt_config->setValue("fullscreen", UISettings::values.fullscreen); 770 WriteSetting("fullscreen", UISettings::values.fullscreen, false);
755 qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar); 771 WriteSetting("displayTitleBars", UISettings::values.display_titlebar, true);
756 qt_config->setValue("showFilterBar", UISettings::values.show_filter_bar); 772 WriteSetting("showFilterBar", UISettings::values.show_filter_bar, true);
757 qt_config->setValue("showStatusBar", UISettings::values.show_status_bar); 773 WriteSetting("showStatusBar", UISettings::values.show_status_bar, true);
758 qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing); 774 WriteSetting("confirmClose", UISettings::values.confirm_before_closing, true);
759 qt_config->setValue("firstStart", UISettings::values.first_start); 775 WriteSetting("firstStart", UISettings::values.first_start, true);
760 qt_config->setValue("calloutFlags", UISettings::values.callout_flags); 776 WriteSetting("calloutFlags", UISettings::values.callout_flags, 0);
761 qt_config->setValue("showConsole", UISettings::values.show_console); 777 WriteSetting("showConsole", UISettings::values.show_console, false);
762 qt_config->setValue("profileIndex", UISettings::values.profile_index); 778 WriteSetting("profileIndex", UISettings::values.profile_index, 0);
763 qt_config->endGroup(); 779 qt_config->endGroup();
764} 780}
765 781
782QVariant Config::ReadSetting(const QString& name) const {
783 return qt_config->value(name);
784}
785
786QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const {
787 QVariant result;
788 if (qt_config->value(name + "/default", false).toBool()) {
789 result = default_value;
790 } else {
791 result = qt_config->value(name, default_value);
792 }
793 return result;
794}
795
796void Config::WriteSetting(const QString& name, const QVariant& value) {
797 qt_config->setValue(name, value);
798}
799
800void Config::WriteSetting(const QString& name, const QVariant& value,
801 const QVariant& default_value) {
802 qt_config->setValue(name + "/default", value == default_value);
803 qt_config->setValue(name, value);
804}
805
766void Config::Reload() { 806void Config::Reload() {
767 ReadValues(); 807 ReadValues();
808 // To apply default value changes
809 SaveValues();
768 Settings::Apply(); 810 Settings::Apply();
769} 811}
770 812
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/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 71683da8e..29f01dfb2 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -383,13 +383,12 @@ void GraphicsSurfaceWidget::OnUpdate() {
383 // TODO: Implement a good way to visualize alpha components! 383 // TODO: Implement a good way to visualize alpha components!
384 384
385 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); 385 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
386 std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
387 386
388 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. 387 // 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. 388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
390 auto unswizzled_data = Tegra::Texture::UnswizzleTexture( 389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
391 *address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, 390 gpu.MemoryManager().GetPointer(surface_address), 1, 1,
392 surface_height, 1U); 391 Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U);
393 392
394 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
395 surface_width, surface_height); 394 surface_width, surface_height);
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);