diff options
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/common/concepts.h | 4 | ||||
| -rw-r--r-- | src/common/div_ceil.h | 10 | ||||
| -rw-r--r-- | src/common/memory_hook.cpp | 11 | ||||
| -rw-r--r-- | src/common/memory_hook.h | 47 | ||||
| -rw-r--r-- | src/common/page_table.cpp | 10 | ||||
| -rw-r--r-- | src/common/page_table.h | 88 | ||||
| -rw-r--r-- | src/common/swap.h | 4 | ||||
| -rw-r--r-- | src/common/thread_worker.cpp | 58 | ||||
| -rw-r--r-- | src/common/thread_worker.h | 30 | ||||
| -rw-r--r-- | src/common/virtual_buffer.h | 10 |
11 files changed, 167 insertions, 109 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 943ff996e..2c2bd2ee8 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -135,8 +135,6 @@ add_library(common STATIC | |||
| 135 | math_util.h | 135 | math_util.h |
| 136 | memory_detect.cpp | 136 | memory_detect.cpp |
| 137 | memory_detect.h | 137 | memory_detect.h |
| 138 | memory_hook.cpp | ||
| 139 | memory_hook.h | ||
| 140 | microprofile.cpp | 138 | microprofile.cpp |
| 141 | microprofile.h | 139 | microprofile.h |
| 142 | microprofileui.h | 140 | microprofileui.h |
| @@ -162,6 +160,8 @@ add_library(common STATIC | |||
| 162 | thread.cpp | 160 | thread.cpp |
| 163 | thread.h | 161 | thread.h |
| 164 | thread_queue_list.h | 162 | thread_queue_list.h |
| 163 | thread_worker.cpp | ||
| 164 | thread_worker.h | ||
| 165 | threadsafe_queue.h | 165 | threadsafe_queue.h |
| 166 | time_zone.cpp | 166 | time_zone.cpp |
| 167 | time_zone.h | 167 | time_zone.h |
diff --git a/src/common/concepts.h b/src/common/concepts.h index 5bef3ad67..aa08065a7 100644 --- a/src/common/concepts.h +++ b/src/common/concepts.h | |||
| @@ -31,4 +31,8 @@ concept DerivedFrom = requires { | |||
| 31 | std::is_convertible_v<const volatile Derived*, const volatile Base*>; | 31 | std::is_convertible_v<const volatile Derived*, const volatile Base*>; |
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| 34 | // TODO: Replace with std::convertible_to when libc++ implements it. | ||
| 35 | template <typename From, typename To> | ||
| 36 | concept ConvertibleTo = std::is_convertible_v<From, To>; | ||
| 37 | |||
| 34 | } // namespace Common | 38 | } // namespace Common |
diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h index 6b2c48f91..95e1489a9 100644 --- a/src/common/div_ceil.h +++ b/src/common/div_ceil.h | |||
| @@ -11,16 +11,16 @@ namespace Common { | |||
| 11 | 11 | ||
| 12 | /// Ceiled integer division. | 12 | /// Ceiled integer division. |
| 13 | template <typename N, typename D> | 13 | template <typename N, typename D> |
| 14 | requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeil( | 14 | requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number, |
| 15 | N number, D divisor) { | 15 | D divisor) { |
| 16 | return (static_cast<D>(number) + divisor - 1) / divisor; | 16 | return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | /// Ceiled integer division with logarithmic divisor in base 2 | 19 | /// Ceiled integer division with logarithmic divisor in base 2 |
| 20 | template <typename N, typename D> | 20 | template <typename N, typename D> |
| 21 | requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeilLog2( | 21 | requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2( |
| 22 | N value, D alignment_log2) { | 22 | N value, D alignment_log2) { |
| 23 | return (static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2; | 23 | return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | } // namespace Common | 26 | } // namespace Common |
diff --git a/src/common/memory_hook.cpp b/src/common/memory_hook.cpp deleted file mode 100644 index 3986986d6..000000000 --- a/src/common/memory_hook.cpp +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/memory_hook.h" | ||
| 6 | |||
| 7 | namespace Common { | ||
| 8 | |||
| 9 | MemoryHook::~MemoryHook() = default; | ||
| 10 | |||
| 11 | } // namespace Common | ||
diff --git a/src/common/memory_hook.h b/src/common/memory_hook.h deleted file mode 100644 index adaa4c2c5..000000000 --- a/src/common/memory_hook.h +++ /dev/null | |||
| @@ -1,47 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <optional> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Memory hooks have two purposes: | ||
| 16 | * 1. To allow reads and writes to a region of memory to be intercepted. This is used to implement | ||
| 17 | * texture forwarding and memory breakpoints for debugging. | ||
| 18 | * 2. To allow for the implementation of MMIO devices. | ||
| 19 | * | ||
| 20 | * A hook may be mapped to multiple regions of memory. | ||
| 21 | * | ||
| 22 | * If a std::nullopt or false is returned from a function, the read/write request is passed through | ||
| 23 | * to the underlying memory region. | ||
| 24 | */ | ||
| 25 | class MemoryHook { | ||
| 26 | public: | ||
| 27 | virtual ~MemoryHook(); | ||
| 28 | |||
| 29 | virtual std::optional<bool> IsValidAddress(VAddr addr) = 0; | ||
| 30 | |||
| 31 | virtual std::optional<u8> Read8(VAddr addr) = 0; | ||
| 32 | virtual std::optional<u16> Read16(VAddr addr) = 0; | ||
| 33 | virtual std::optional<u32> Read32(VAddr addr) = 0; | ||
| 34 | virtual std::optional<u64> Read64(VAddr addr) = 0; | ||
| 35 | |||
| 36 | virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0; | ||
| 37 | |||
| 38 | virtual bool Write8(VAddr addr, u8 data) = 0; | ||
| 39 | virtual bool Write16(VAddr addr, u16 data) = 0; | ||
| 40 | virtual bool Write32(VAddr addr, u32 data) = 0; | ||
| 41 | virtual bool Write64(VAddr addr, u64 data) = 0; | ||
| 42 | |||
| 43 | virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0; | ||
| 44 | }; | ||
| 45 | |||
| 46 | using MemoryHookPointer = std::shared_ptr<MemoryHook>; | ||
| 47 | } // namespace Common | ||
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index bccea0894..8fd8620fd 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp | |||
| @@ -10,16 +10,10 @@ PageTable::PageTable() = default; | |||
| 10 | 10 | ||
| 11 | PageTable::~PageTable() noexcept = default; | 11 | PageTable::~PageTable() noexcept = default; |
| 12 | 12 | ||
| 13 | void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, | 13 | void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) { |
| 14 | bool has_attribute) { | 14 | const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)}; |
| 15 | const std::size_t num_page_table_entries{1ULL | ||
| 16 | << (address_space_width_in_bits - page_size_in_bits)}; | ||
| 17 | pointers.resize(num_page_table_entries); | 15 | pointers.resize(num_page_table_entries); |
| 18 | backing_addr.resize(num_page_table_entries); | 16 | backing_addr.resize(num_page_table_entries); |
| 19 | |||
| 20 | if (has_attribute) { | ||
| 21 | attributes.resize(num_page_table_entries); | ||
| 22 | } | ||
| 23 | } | 17 | } |
| 24 | 18 | ||
| 25 | } // namespace Common | 19 | } // namespace Common |
diff --git a/src/common/page_table.h b/src/common/page_table.h index 9754fabf9..61c5552e0 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h | |||
| @@ -4,10 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <tuple> | 8 | #include <tuple> |
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "common/memory_hook.h" | ||
| 11 | #include "common/virtual_buffer.h" | 11 | #include "common/virtual_buffer.h" |
| 12 | 12 | ||
| 13 | namespace Common { | 13 | namespace Common { |
| @@ -20,27 +20,6 @@ enum class PageType : u8 { | |||
| 20 | /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and | 20 | /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and |
| 21 | /// invalidation | 21 | /// invalidation |
| 22 | RasterizerCachedMemory, | 22 | RasterizerCachedMemory, |
| 23 | /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. | ||
| 24 | Special, | ||
| 25 | /// Page is allocated for use. | ||
| 26 | Allocated, | ||
| 27 | }; | ||
| 28 | |||
| 29 | struct SpecialRegion { | ||
| 30 | enum class Type { | ||
| 31 | DebugHook, | ||
| 32 | IODevice, | ||
| 33 | } type; | ||
| 34 | |||
| 35 | MemoryHookPointer handler; | ||
| 36 | |||
| 37 | [[nodiscard]] bool operator<(const SpecialRegion& other) const { | ||
| 38 | return std::tie(type, handler) < std::tie(other.type, other.handler); | ||
| 39 | } | ||
| 40 | |||
| 41 | [[nodiscard]] bool operator==(const SpecialRegion& other) const { | ||
| 42 | return std::tie(type, handler) == std::tie(other.type, other.handler); | ||
| 43 | } | ||
| 44 | }; | 23 | }; |
| 45 | 24 | ||
| 46 | /** | 25 | /** |
| @@ -48,6 +27,59 @@ struct SpecialRegion { | |||
| 48 | * mimics the way a real CPU page table works. | 27 | * mimics the way a real CPU page table works. |
| 49 | */ | 28 | */ |
| 50 | struct PageTable { | 29 | struct PageTable { |
| 30 | /// Number of bits reserved for attribute tagging. | ||
| 31 | /// This can be at most the guaranteed alignment of the pointers in the page table. | ||
| 32 | static constexpr int ATTRIBUTE_BITS = 2; | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Pair of host pointer and page type attribute. | ||
| 36 | * This uses the lower bits of a given pointer to store the attribute tag. | ||
| 37 | * Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method | ||
| 38 | * call. In other words, they are guaranteed to be synchronized at all times. | ||
| 39 | */ | ||
| 40 | class PageInfo { | ||
| 41 | public: | ||
| 42 | /// Returns the page pointer | ||
| 43 | [[nodiscard]] u8* Pointer() const noexcept { | ||
| 44 | return ExtractPointer(raw.load(std::memory_order_relaxed)); | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Returns the page type attribute | ||
| 48 | [[nodiscard]] PageType Type() const noexcept { | ||
| 49 | return ExtractType(raw.load(std::memory_order_relaxed)); | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Returns the page pointer and attribute pair, extracted from the same atomic read | ||
| 53 | [[nodiscard]] std::pair<u8*, PageType> PointerType() const noexcept { | ||
| 54 | const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed); | ||
| 55 | return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)}; | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Returns the raw representation of the page information. | ||
| 59 | /// Use ExtractPointer and ExtractType to unpack the value. | ||
| 60 | [[nodiscard]] uintptr_t Raw() const noexcept { | ||
| 61 | return raw.load(std::memory_order_relaxed); | ||
| 62 | } | ||
| 63 | |||
| 64 | /// Write a page pointer and type pair atomically | ||
| 65 | void Store(u8* pointer, PageType type) noexcept { | ||
| 66 | raw.store(reinterpret_cast<uintptr_t>(pointer) | static_cast<uintptr_t>(type)); | ||
| 67 | } | ||
| 68 | |||
| 69 | /// Unpack a pointer from a page info raw representation | ||
| 70 | [[nodiscard]] static u8* ExtractPointer(uintptr_t raw) noexcept { | ||
| 71 | return reinterpret_cast<u8*>(raw & (~uintptr_t{0} << ATTRIBUTE_BITS)); | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Unpack a page type from a page info raw representation | ||
| 75 | [[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept { | ||
| 76 | return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1)); | ||
| 77 | } | ||
| 78 | |||
| 79 | private: | ||
| 80 | std::atomic<uintptr_t> raw; | ||
| 81 | }; | ||
| 82 | |||
| 51 | PageTable(); | 83 | PageTable(); |
| 52 | ~PageTable() noexcept; | 84 | ~PageTable() noexcept; |
| 53 | 85 | ||
| @@ -58,25 +90,21 @@ struct PageTable { | |||
| 58 | PageTable& operator=(PageTable&&) noexcept = default; | 90 | PageTable& operator=(PageTable&&) noexcept = default; |
| 59 | 91 | ||
| 60 | /** | 92 | /** |
| 61 | * Resizes the page table to be able to accomodate enough pages within | 93 | * Resizes the page table to be able to accommodate enough pages within |
| 62 | * a given address space. | 94 | * a given address space. |
| 63 | * | 95 | * |
| 64 | * @param address_space_width_in_bits The address size width in bits. | 96 | * @param address_space_width_in_bits The address size width in bits. |
| 65 | * @param page_size_in_bits The page size in bits. | 97 | * @param page_size_in_bits The page size in bits. |
| 66 | * @param has_attribute Whether or not this page has any backing attributes. | ||
| 67 | */ | 98 | */ |
| 68 | void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, | 99 | void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits); |
| 69 | bool has_attribute); | ||
| 70 | 100 | ||
| 71 | /** | 101 | /** |
| 72 | * Vector of memory pointers backing each page. An entry can only be non-null if the | 102 | * Vector of memory pointers backing each page. An entry can only be non-null if the |
| 73 | * corresponding entry in the `attributes` vector is of type `Memory`. | 103 | * corresponding attribute element is of type `Memory`. |
| 74 | */ | 104 | */ |
| 75 | VirtualBuffer<u8*> pointers; | 105 | VirtualBuffer<PageInfo> pointers; |
| 76 | 106 | ||
| 77 | VirtualBuffer<u64> backing_addr; | 107 | VirtualBuffer<u64> backing_addr; |
| 78 | |||
| 79 | VirtualBuffer<PageType> attributes; | ||
| 80 | }; | 108 | }; |
| 81 | 109 | ||
| 82 | } // namespace Common | 110 | } // namespace Common |
diff --git a/src/common/swap.h b/src/common/swap.h index 7665942a2..a80e191dc 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -394,7 +394,7 @@ public: | |||
| 394 | template <typename S, typename T2, typename F2> | 394 | template <typename S, typename T2, typename F2> |
| 395 | friend S operator%(const S& p, const swapped_t v); | 395 | friend S operator%(const S& p, const swapped_t v); |
| 396 | 396 | ||
| 397 | // Arithmetics + assignements | 397 | // Arithmetics + assignments |
| 398 | template <typename S, typename T2, typename F2> | 398 | template <typename S, typename T2, typename F2> |
| 399 | friend S operator+=(const S& p, const swapped_t v); | 399 | friend S operator+=(const S& p, const swapped_t v); |
| 400 | 400 | ||
| @@ -451,7 +451,7 @@ S operator%(const S& i, const swap_struct_t<T, F> v) { | |||
| 451 | return i % v.swap(); | 451 | return i % v.swap(); |
| 452 | } | 452 | } |
| 453 | 453 | ||
| 454 | // Arithmetics + assignements | 454 | // Arithmetics + assignments |
| 455 | template <typename S, typename T, typename F> | 455 | template <typename S, typename T, typename F> |
| 456 | S& operator+=(S& i, const swap_struct_t<T, F> v) { | 456 | S& operator+=(S& i, const swap_struct_t<T, F> v) { |
| 457 | i += v.swap(); | 457 | i += v.swap(); |
diff --git a/src/common/thread_worker.cpp b/src/common/thread_worker.cpp new file mode 100644 index 000000000..8f9bf447a --- /dev/null +++ b/src/common/thread_worker.cpp | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/thread.h" | ||
| 6 | #include "common/thread_worker.h" | ||
| 7 | |||
| 8 | namespace Common { | ||
| 9 | |||
| 10 | ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) { | ||
| 11 | for (std::size_t i = 0; i < num_workers; ++i) | ||
| 12 | threads.emplace_back([this, thread_name{std::string{name}}] { | ||
| 13 | Common::SetCurrentThreadName(thread_name.c_str()); | ||
| 14 | |||
| 15 | // Wait for first request | ||
| 16 | { | ||
| 17 | std::unique_lock lock{queue_mutex}; | ||
| 18 | condition.wait(lock, [this] { return stop || !requests.empty(); }); | ||
| 19 | } | ||
| 20 | |||
| 21 | while (true) { | ||
| 22 | std::function<void()> task; | ||
| 23 | |||
| 24 | { | ||
| 25 | std::unique_lock lock{queue_mutex}; | ||
| 26 | condition.wait(lock, [this] { return stop || !requests.empty(); }); | ||
| 27 | if (stop || requests.empty()) { | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | task = std::move(requests.front()); | ||
| 31 | requests.pop(); | ||
| 32 | } | ||
| 33 | |||
| 34 | task(); | ||
| 35 | } | ||
| 36 | }); | ||
| 37 | } | ||
| 38 | |||
| 39 | ThreadWorker::~ThreadWorker() { | ||
| 40 | { | ||
| 41 | std::unique_lock lock{queue_mutex}; | ||
| 42 | stop = true; | ||
| 43 | } | ||
| 44 | condition.notify_all(); | ||
| 45 | for (std::thread& thread : threads) { | ||
| 46 | thread.join(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void ThreadWorker::QueueWork(std::function<void()>&& work) { | ||
| 51 | { | ||
| 52 | std::unique_lock lock{queue_mutex}; | ||
| 53 | requests.emplace(work); | ||
| 54 | } | ||
| 55 | condition.notify_one(); | ||
| 56 | } | ||
| 57 | |||
| 58 | } // namespace Common | ||
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h new file mode 100644 index 000000000..f1859971f --- /dev/null +++ b/src/common/thread_worker.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | // Copyright 2020 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 <atomic> | ||
| 8 | #include <functional> | ||
| 9 | #include <mutex> | ||
| 10 | #include <string> | ||
| 11 | #include <vector> | ||
| 12 | #include <queue> | ||
| 13 | |||
| 14 | namespace Common { | ||
| 15 | |||
| 16 | class ThreadWorker final { | ||
| 17 | public: | ||
| 18 | explicit ThreadWorker(std::size_t num_workers, const std::string& name); | ||
| 19 | ~ThreadWorker(); | ||
| 20 | void QueueWork(std::function<void()>&& work); | ||
| 21 | |||
| 22 | private: | ||
| 23 | std::vector<std::thread> threads; | ||
| 24 | std::queue<std::function<void()>> requests; | ||
| 25 | std::mutex queue_mutex; | ||
| 26 | std::condition_variable condition; | ||
| 27 | std::atomic_bool stop{}; | ||
| 28 | }; | ||
| 29 | |||
| 30 | } // namespace Common | ||
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h index 91d430036..fb1a6f81f 100644 --- a/src/common/virtual_buffer.h +++ b/src/common/virtual_buffer.h | |||
| @@ -15,10 +15,12 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept; | |||
| 15 | template <typename T> | 15 | template <typename T> |
| 16 | class VirtualBuffer final { | 16 | class VirtualBuffer final { |
| 17 | public: | 17 | public: |
| 18 | static_assert( | 18 | // TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible |
| 19 | std::is_trivially_constructible_v<T>, | 19 | // using std::atomic_ref once libc++ has support for it |
| 20 | "T must be trivially constructible, as non-trivial constructors will not be executed " | 20 | // static_assert( |
| 21 | "with the current allocator"); | 21 | // std::is_trivially_constructible_v<T>, |
| 22 | // "T must be trivially constructible, as non-trivial constructors will not be executed " | ||
| 23 | // "with the current allocator"); | ||
| 22 | 24 | ||
| 23 | constexpr VirtualBuffer() = default; | 25 | constexpr VirtualBuffer() = default; |
| 24 | explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { | 26 | explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { |