summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/alignment.h7
-rw-r--r--src/common/common_funcs.h32
-rw-r--r--src/common/file_util.cpp37
-rw-r--r--src/common/file_util.h14
-rw-r--r--src/common/page_table.cpp34
-rw-r--r--src/common/page_table.h37
-rw-r--r--src/common/scope_exit.h9
-rw-r--r--src/common/virtual_buffer.cpp52
-rw-r--r--src/common/virtual_buffer.h58
10 files changed, 218 insertions, 64 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index eeceaa655..6ffc612e7 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -155,6 +155,8 @@ add_library(common STATIC
155 uuid.cpp 155 uuid.cpp
156 uuid.h 156 uuid.h
157 vector_math.h 157 vector_math.h
158 virtual_buffer.cpp
159 virtual_buffer.h
158 web_result.h 160 web_result.h
159 zstd_compression.cpp 161 zstd_compression.cpp
160 zstd_compression.h 162 zstd_compression.h
diff --git a/src/common/alignment.h b/src/common/alignment.h
index cdd4833f8..f8c49e079 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -38,6 +38,13 @@ constexpr bool IsWordAligned(T value) {
38 return (value & 0b11) == 0; 38 return (value & 0b11) == 0;
39} 39}
40 40
41template <typename T>
42constexpr bool IsAligned(T value, std::size_t alignment) {
43 using U = typename std::make_unsigned<T>::type;
44 const U mask = static_cast<U>(alignment - 1);
45 return (value & mask) == 0;
46}
47
41template <typename T, std::size_t Align = 16> 48template <typename T, std::size_t Align = 16>
42class AlignmentAllocator { 49class AlignmentAllocator {
43public: 50public:
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 052254678..88cf5250a 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -55,6 +55,38 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
55// Defined in Misc.cpp. 55// Defined in Misc.cpp.
56std::string GetLastErrorMsg(); 56std::string GetLastErrorMsg();
57 57
58#define DECLARE_ENUM_FLAG_OPERATORS(type) \
59 constexpr type operator|(type a, type b) noexcept { \
60 using T = std::underlying_type_t<type>; \
61 return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \
62 } \
63 constexpr type operator&(type a, type b) noexcept { \
64 using T = std::underlying_type_t<type>; \
65 return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
66 } \
67 constexpr type& operator|=(type& a, type b) noexcept { \
68 using T = std::underlying_type_t<type>; \
69 a = static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \
70 return a; \
71 } \
72 constexpr type& operator&=(type& a, type b) noexcept { \
73 using T = std::underlying_type_t<type>; \
74 a = static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
75 return a; \
76 } \
77 constexpr type operator~(type key) noexcept { \
78 using T = std::underlying_type_t<type>; \
79 return static_cast<type>(~static_cast<T>(key)); \
80 } \
81 constexpr bool True(type key) noexcept { \
82 using T = std::underlying_type_t<type>; \
83 return static_cast<T>(key) != 0; \
84 } \
85 constexpr bool False(type key) noexcept { \
86 using T = std::underlying_type_t<type>; \
87 return static_cast<T>(key) == 0; \
88 }
89
58namespace Common { 90namespace Common {
59 91
60constexpr u32 MakeMagic(char a, char b, char c, char d) { 92constexpr u32 MakeMagic(char a, char b, char c, char d) {
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 35eee0096..45b750e1e 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -888,7 +888,14 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
888 } 888 }
889 889
890 std::replace(path.begin(), path.end(), type1, type2); 890 std::replace(path.begin(), path.end(), type1, type2);
891 path.erase(std::unique(path.begin(), path.end(), 891
892 auto start = path.begin();
893#ifdef _WIN32
894 // allow network paths which start with a double backslash (e.g. \\server\share)
895 if (start != path.end())
896 ++start;
897#endif
898 path.erase(std::unique(start, path.end(),
892 [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }), 899 [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
893 path.end()); 900 path.end());
894 return std::string(RemoveTrailingSlash(path)); 901 return std::string(RemoveTrailingSlash(path));
@@ -967,6 +974,34 @@ bool IOFile::Flush() {
967 return IsOpen() && 0 == std::fflush(m_file); 974 return IsOpen() && 0 == std::fflush(m_file);
968} 975}
969 976
977std::size_t IOFile::ReadImpl(void* data, std::size_t length, std::size_t data_size) const {
978 if (!IsOpen()) {
979 return std::numeric_limits<std::size_t>::max();
980 }
981
982 if (length == 0) {
983 return 0;
984 }
985
986 DEBUG_ASSERT(data != nullptr);
987
988 return std::fread(data, data_size, length, m_file);
989}
990
991std::size_t IOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
992 if (!IsOpen()) {
993 return std::numeric_limits<std::size_t>::max();
994 }
995
996 if (length == 0) {
997 return 0;
998 }
999
1000 DEBUG_ASSERT(data != nullptr);
1001
1002 return std::fwrite(data, data_size, length, m_file);
1003}
1004
970bool IOFile::Resize(u64 size) { 1005bool IOFile::Resize(u64 size) {
971 return IsOpen() && 0 == 1006 return IsOpen() && 0 ==
972#ifdef _WIN32 1007#ifdef _WIN32
diff --git a/src/common/file_util.h b/src/common/file_util.h
index cde7ddf2d..f7a0c33fa 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -222,22 +222,15 @@ public:
222 static_assert(std::is_trivially_copyable_v<T>, 222 static_assert(std::is_trivially_copyable_v<T>,
223 "Given array does not consist of trivially copyable objects"); 223 "Given array does not consist of trivially copyable objects");
224 224
225 if (!IsOpen()) { 225 return ReadImpl(data, length, sizeof(T));
226 return std::numeric_limits<std::size_t>::max();
227 }
228
229 return std::fread(data, sizeof(T), length, m_file);
230 } 226 }
231 227
232 template <typename T> 228 template <typename T>
233 std::size_t WriteArray(const T* data, std::size_t length) { 229 std::size_t WriteArray(const T* data, std::size_t length) {
234 static_assert(std::is_trivially_copyable_v<T>, 230 static_assert(std::is_trivially_copyable_v<T>,
235 "Given array does not consist of trivially copyable objects"); 231 "Given array does not consist of trivially copyable objects");
236 if (!IsOpen()) {
237 return std::numeric_limits<std::size_t>::max();
238 }
239 232
240 return std::fwrite(data, sizeof(T), length, m_file); 233 return WriteImpl(data, length, sizeof(T));
241 } 234 }
242 235
243 template <typename T> 236 template <typename T>
@@ -278,6 +271,9 @@ public:
278 } 271 }
279 272
280private: 273private:
274 std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) const;
275 std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
276
281 std::FILE* m_file = nullptr; 277 std::FILE* m_file = nullptr;
282}; 278};
283 279
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 566b57b62..e5d3090d5 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -6,36 +6,20 @@
6 6
7namespace Common { 7namespace Common {
8 8
9PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {} 9PageTable::PageTable() = default;
10 10
11PageTable::~PageTable() = default; 11PageTable::~PageTable() = default;
12 12
13void PageTable::Resize(std::size_t address_space_width_in_bits) { 13void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
14 const std::size_t num_page_table_entries = 1ULL 14 bool has_attribute) {
15 << (address_space_width_in_bits - page_size_in_bits); 15 const std::size_t num_page_table_entries{1ULL
16 16 << (address_space_width_in_bits - page_size_in_bits)};
17 pointers.resize(num_page_table_entries); 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
29BackingPageTable::BackingPageTable(std::size_t page_size_in_bits) : PageTable{page_size_in_bits} {}
30
31BackingPageTable::~BackingPageTable() = default;
32
33void BackingPageTable::Resize(std::size_t address_space_width_in_bits) {
34 PageTable::Resize(address_space_width_in_bits);
35 const std::size_t num_page_table_entries = 1ULL
36 << (address_space_width_in_bits - page_size_in_bits);
37 backing_addr.resize(num_page_table_entries); 18 backing_addr.resize(num_page_table_entries);
38 backing_addr.shrink_to_fit(); 19
20 if (has_attribute) {
21 attributes.resize(num_page_table_entries);
22 }
39} 23}
40 24
41} // namespace Common 25} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
index dbc272ab7..1e8bd3187 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -5,9 +5,12 @@
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <vector>
8
8#include <boost/icl/interval_map.hpp> 9#include <boost/icl/interval_map.hpp>
10
9#include "common/common_types.h" 11#include "common/common_types.h"
10#include "common/memory_hook.h" 12#include "common/memory_hook.h"
13#include "common/virtual_buffer.h"
11 14
12namespace Common { 15namespace Common {
13 16
@@ -47,7 +50,7 @@ struct SpecialRegion {
47 * mimics the way a real CPU page table works. 50 * mimics the way a real CPU page table works.
48 */ 51 */
49struct PageTable { 52struct PageTable {
50 explicit PageTable(std::size_t page_size_in_bits); 53 PageTable();
51 ~PageTable(); 54 ~PageTable();
52 55
53 /** 56 /**
@@ -56,40 +59,18 @@ struct PageTable {
56 * 59 *
57 * @param address_space_width_in_bits The address size width in bits. 60 * @param address_space_width_in_bits The address size width in bits.
58 */ 61 */
59 void Resize(std::size_t address_space_width_in_bits); 62 void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
63 bool has_attribute);
60 64
61 /** 65 /**
62 * Vector of memory pointers backing each page. An entry can only be non-null if the 66 * Vector of memory pointers backing each page. An entry can only be non-null if the
63 * corresponding entry in the `attributes` vector is of type `Memory`. 67 * corresponding entry in the `attributes` vector is of type `Memory`.
64 */ 68 */
65 std::vector<u8*> pointers; 69 VirtualBuffer<u8*> pointers;
66
67 /**
68 * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
69 * of type `Special`.
70 */
71 boost::icl::interval_map<u64, std::set<SpecialRegion>> special_regions;
72
73 /**
74 * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
75 * the corresponding entry in `pointers` MUST be set to null.
76 */
77 std::vector<PageType> attributes;
78
79 const std::size_t page_size_in_bits{};
80};
81
82/**
83 * A more advanced Page Table with the ability to save a backing address when using it
84 * depends on another MMU.
85 */
86struct BackingPageTable : PageTable {
87 explicit BackingPageTable(std::size_t page_size_in_bits);
88 ~BackingPageTable();
89 70
90 void Resize(std::size_t address_space_width_in_bits); 71 VirtualBuffer<u64> backing_addr;
91 72
92 std::vector<u64> backing_addr; 73 VirtualBuffer<PageType> attributes;
93}; 74};
94 75
95} // namespace Common 76} // namespace Common
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
index 1176a72b1..68ef5f197 100644
--- a/src/common/scope_exit.h
+++ b/src/common/scope_exit.h
@@ -12,10 +12,17 @@ template <typename Func>
12struct ScopeExitHelper { 12struct ScopeExitHelper {
13 explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {} 13 explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}
14 ~ScopeExitHelper() { 14 ~ScopeExitHelper() {
15 func(); 15 if (active) {
16 func();
17 }
18 }
19
20 void Cancel() {
21 active = false;
16 } 22 }
17 23
18 Func func; 24 Func func;
25 bool active{true};
19}; 26};
20 27
21template <typename Func> 28template <typename Func>
diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp
new file mode 100644
index 000000000..b426f4747
--- /dev/null
+++ b/src/common/virtual_buffer.cpp
@@ -0,0 +1,52 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#ifdef _WIN32
6#include <windows.h>
7#else
8#include <stdio.h>
9#include <sys/mman.h>
10#include <sys/types.h>
11#if defined __APPLE__ || defined __FreeBSD__ || defined __OpenBSD__
12#include <sys/sysctl.h>
13#elif defined __HAIKU__
14#include <OS.h>
15#else
16#include <sys/sysinfo.h>
17#endif
18#endif
19
20#include "common/assert.h"
21#include "common/virtual_buffer.h"
22
23namespace Common {
24
25void* AllocateMemoryPages(std::size_t size) {
26#ifdef _WIN32
27 void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)};
28#else
29 void* base{mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)};
30
31 if (base == MAP_FAILED) {
32 base = nullptr;
33 }
34#endif
35
36 ASSERT(base);
37
38 return base;
39}
40
41void FreeMemoryPages(void* base, std::size_t size) {
42 if (!base) {
43 return;
44 }
45#ifdef _WIN32
46 ASSERT(VirtualFree(base, 0, MEM_RELEASE));
47#else
48 ASSERT(munmap(base, size) == 0);
49#endif
50}
51
52} // namespace Common
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h
new file mode 100644
index 000000000..da064e59e
--- /dev/null
+++ b/src/common/virtual_buffer.h
@@ -0,0 +1,58 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8
9namespace Common {
10
11void* AllocateMemoryPages(std::size_t size);
12void FreeMemoryPages(void* base, std::size_t size);
13
14template <typename T>
15class VirtualBuffer final : NonCopyable {
16public:
17 constexpr VirtualBuffer() = default;
18 explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
19 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
20 }
21
22 ~VirtualBuffer() {
23 FreeMemoryPages(base_ptr, alloc_size);
24 }
25
26 void resize(std::size_t count) {
27 FreeMemoryPages(base_ptr, alloc_size);
28
29 alloc_size = count * sizeof(T);
30 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
31 }
32
33 constexpr const T& operator[](std::size_t index) const {
34 return base_ptr[index];
35 }
36
37 constexpr T& operator[](std::size_t index) {
38 return base_ptr[index];
39 }
40
41 constexpr T* data() {
42 return base_ptr;
43 }
44
45 constexpr const T* data() const {
46 return base_ptr;
47 }
48
49 constexpr std::size_t size() const {
50 return alloc_size / sizeof(T);
51 }
52
53private:
54 std::size_t alloc_size{};
55 T* base_ptr{};
56};
57
58} // namespace Common