diff options
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/common/alignment.h | 7 | ||||
| -rw-r--r-- | src/common/common_funcs.h | 32 | ||||
| -rw-r--r-- | src/common/file_util.cpp | 37 | ||||
| -rw-r--r-- | src/common/file_util.h | 14 | ||||
| -rw-r--r-- | src/common/page_table.cpp | 34 | ||||
| -rw-r--r-- | src/common/page_table.h | 37 | ||||
| -rw-r--r-- | src/common/scope_exit.h | 9 | ||||
| -rw-r--r-- | src/common/virtual_buffer.cpp | 52 | ||||
| -rw-r--r-- | src/common/virtual_buffer.h | 58 | 
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      uuid.cpp      uuid.h      vector_math.h +    virtual_buffer.cpp +    virtual_buffer.h      web_result.h      zstd_compression.cpp      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) {      return (value & 0b11) == 0;  } +template <typename T> +constexpr bool IsAligned(T value, std::size_t alignment) { +    using U = typename std::make_unsigned<T>::type; +    const U mask = static_cast<U>(alignment - 1); +    return (value & mask) == 0; +} +  template <typename T, std::size_t Align = 16>  class AlignmentAllocator {  public: 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);  // Defined in Misc.cpp.  std::string GetLastErrorMsg(); +#define DECLARE_ENUM_FLAG_OPERATORS(type)                                                          \ +    constexpr type operator|(type a, type b) noexcept {                                            \ +        using T = std::underlying_type_t<type>;                                                    \ +        return static_cast<type>(static_cast<T>(a) | static_cast<T>(b));                           \ +    }                                                                                              \ +    constexpr type operator&(type a, type b) noexcept {                                            \ +        using T = std::underlying_type_t<type>;                                                    \ +        return static_cast<type>(static_cast<T>(a) & static_cast<T>(b));                           \ +    }                                                                                              \ +    constexpr type& operator|=(type& a, type b) noexcept {                                         \ +        using T = std::underlying_type_t<type>;                                                    \ +        a = static_cast<type>(static_cast<T>(a) | static_cast<T>(b));                              \ +        return a;                                                                                  \ +    }                                                                                              \ +    constexpr type& operator&=(type& a, type b) noexcept {                                         \ +        using T = std::underlying_type_t<type>;                                                    \ +        a = static_cast<type>(static_cast<T>(a) & static_cast<T>(b));                              \ +        return a;                                                                                  \ +    }                                                                                              \ +    constexpr type operator~(type key) noexcept {                                                  \ +        using T = std::underlying_type_t<type>;                                                    \ +        return static_cast<type>(~static_cast<T>(key));                                            \ +    }                                                                                              \ +    constexpr bool True(type key) noexcept {                                                       \ +        using T = std::underlying_type_t<type>;                                                    \ +        return static_cast<T>(key) != 0;                                                           \ +    }                                                                                              \ +    constexpr bool False(type key) noexcept {                                                      \ +        using T = std::underlying_type_t<type>;                                                    \ +        return static_cast<T>(key) == 0;                                                           \ +    } +  namespace Common {  constexpr 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      }      std::replace(path.begin(), path.end(), type1, type2); -    path.erase(std::unique(path.begin(), path.end(), + +    auto start = path.begin(); +#ifdef _WIN32 +    // allow network paths which start with a double backslash (e.g. \\server\share) +    if (start != path.end()) +        ++start; +#endif +    path.erase(std::unique(start, path.end(),                             [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),                 path.end());      return std::string(RemoveTrailingSlash(path)); @@ -967,6 +974,34 @@ bool IOFile::Flush() {      return IsOpen() && 0 == std::fflush(m_file);  } +std::size_t IOFile::ReadImpl(void* data, std::size_t length, std::size_t data_size) const { +    if (!IsOpen()) { +        return std::numeric_limits<std::size_t>::max(); +    } + +    if (length == 0) { +        return 0; +    } + +    DEBUG_ASSERT(data != nullptr); + +    return std::fread(data, data_size, length, m_file); +} + +std::size_t IOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) { +    if (!IsOpen()) { +        return std::numeric_limits<std::size_t>::max(); +    } + +    if (length == 0) { +        return 0; +    } + +    DEBUG_ASSERT(data != nullptr); + +    return std::fwrite(data, data_size, length, m_file); +} +  bool IOFile::Resize(u64 size) {      return IsOpen() && 0 ==  #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:          static_assert(std::is_trivially_copyable_v<T>,                        "Given array does not consist of trivially copyable objects"); -        if (!IsOpen()) { -            return std::numeric_limits<std::size_t>::max(); -        } - -        return std::fread(data, sizeof(T), length, m_file); +        return ReadImpl(data, length, sizeof(T));      }      template <typename T>      std::size_t WriteArray(const T* data, std::size_t length) {          static_assert(std::is_trivially_copyable_v<T>,                        "Given array does not consist of trivially copyable objects"); -        if (!IsOpen()) { -            return std::numeric_limits<std::size_t>::max(); -        } -        return std::fwrite(data, sizeof(T), length, m_file); +        return WriteImpl(data, length, sizeof(T));      }      template <typename T> @@ -278,6 +271,9 @@ public:      }  private: +    std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) const; +    std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size); +      std::FILE* m_file = nullptr;  }; 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 @@  namespace Common { -PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {} +PageTable::PageTable() = default;  PageTable::~PageTable() = default; -void PageTable::Resize(std::size_t address_space_width_in_bits) { -    const std::size_t num_page_table_entries = 1ULL -                                               << (address_space_width_in_bits - page_size_in_bits); - +void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, +                       bool has_attribute) { +    const std::size_t num_page_table_entries{1ULL +                                             << (address_space_width_in_bits - page_size_in_bits)};      pointers.resize(num_page_table_entries); -    attributes.resize(num_page_table_entries); - -    // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the -    // vector size is subsequently decreased (via resize), the vector might not automatically -    // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for -    // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use. - -    pointers.shrink_to_fit(); -    attributes.shrink_to_fit(); -} - -BackingPageTable::BackingPageTable(std::size_t page_size_in_bits) : PageTable{page_size_in_bits} {} - -BackingPageTable::~BackingPageTable() = default; - -void BackingPageTable::Resize(std::size_t address_space_width_in_bits) { -    PageTable::Resize(address_space_width_in_bits); -    const std::size_t num_page_table_entries = 1ULL -                                               << (address_space_width_in_bits - page_size_in_bits);      backing_addr.resize(num_page_table_entries); -    backing_addr.shrink_to_fit(); + +    if (has_attribute) { +        attributes.resize(num_page_table_entries); +    }  }  } // 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 @@  #pragma once  #include <vector> +  #include <boost/icl/interval_map.hpp> +  #include "common/common_types.h"  #include "common/memory_hook.h" +#include "common/virtual_buffer.h"  namespace Common { @@ -47,7 +50,7 @@ struct SpecialRegion {   * mimics the way a real CPU page table works.   */  struct PageTable { -    explicit PageTable(std::size_t page_size_in_bits); +    PageTable();      ~PageTable();      /** @@ -56,40 +59,18 @@ struct PageTable {       *       * @param address_space_width_in_bits The address size width in bits.       */ -    void Resize(std::size_t address_space_width_in_bits); +    void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, +                bool has_attribute);      /**       * Vector of memory pointers backing each page. An entry can only be non-null if the       * corresponding entry in the `attributes` vector is of type `Memory`.       */ -    std::vector<u8*> pointers; - -    /** -     * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is -     * of type `Special`. -     */ -    boost::icl::interval_map<u64, std::set<SpecialRegion>> special_regions; - -    /** -     * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then -     * the corresponding entry in `pointers` MUST be set to null. -     */ -    std::vector<PageType> attributes; - -    const std::size_t page_size_in_bits{}; -}; - -/** - * A more advanced Page Table with the ability to save a backing address when using it - * depends on another MMU. - */ -struct BackingPageTable : PageTable { -    explicit BackingPageTable(std::size_t page_size_in_bits); -    ~BackingPageTable(); +    VirtualBuffer<u8*> pointers; -    void Resize(std::size_t address_space_width_in_bits); +    VirtualBuffer<u64> backing_addr; -    std::vector<u64> backing_addr; +    VirtualBuffer<PageType> attributes;  };  } // 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>  struct ScopeExitHelper {      explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}      ~ScopeExitHelper() { -        func(); +        if (active) { +            func(); +        } +    } + +    void Cancel() { +        active = false;      }      Func func; +    bool active{true};  };  template <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 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#ifdef _WIN32 +#include <windows.h> +#else +#include <stdio.h> +#include <sys/mman.h> +#include <sys/types.h> +#if defined __APPLE__ || defined __FreeBSD__ || defined __OpenBSD__ +#include <sys/sysctl.h> +#elif defined __HAIKU__ +#include <OS.h> +#else +#include <sys/sysinfo.h> +#endif +#endif + +#include "common/assert.h" +#include "common/virtual_buffer.h" + +namespace Common { + +void* AllocateMemoryPages(std::size_t size) { +#ifdef _WIN32 +    void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)}; +#else +    void* base{mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)}; + +    if (base == MAP_FAILED) { +        base = nullptr; +    } +#endif + +    ASSERT(base); + +    return base; +} + +void FreeMemoryPages(void* base, std::size_t size) { +    if (!base) { +        return; +    } +#ifdef _WIN32 +    ASSERT(VirtualFree(base, 0, MEM_RELEASE)); +#else +    ASSERT(munmap(base, size) == 0); +#endif +} + +} // 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 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_funcs.h" + +namespace Common { + +void* AllocateMemoryPages(std::size_t size); +void FreeMemoryPages(void* base, std::size_t size); + +template <typename T> +class VirtualBuffer final : NonCopyable { +public: +    constexpr VirtualBuffer() = default; +    explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { +        base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); +    } + +    ~VirtualBuffer() { +        FreeMemoryPages(base_ptr, alloc_size); +    } + +    void resize(std::size_t count) { +        FreeMemoryPages(base_ptr, alloc_size); + +        alloc_size = count * sizeof(T); +        base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); +    } + +    constexpr const T& operator[](std::size_t index) const { +        return base_ptr[index]; +    } + +    constexpr T& operator[](std::size_t index) { +        return base_ptr[index]; +    } + +    constexpr T* data() { +        return base_ptr; +    } + +    constexpr const T* data() const { +        return base_ptr; +    } + +    constexpr std::size_t size() const { +        return alloc_size / sizeof(T); +    } + +private: +    std::size_t alloc_size{}; +    T* base_ptr{}; +}; + +} // namespace Common | 
