MemArena.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // Copyright 2008 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <cstddef>
  5. #include <string_view>
  6. #include <vector>
  7. #ifdef __APPLE__
  8. #include <mach/mach.h>
  9. #endif
  10. #include "Common/CommonTypes.h"
  11. #include "Common/DynamicLibrary.h"
  12. namespace Common
  13. {
  14. #ifdef _WIN32
  15. struct WindowsMemoryRegion;
  16. struct WindowsMemoryFunctions
  17. {
  18. Common::DynamicLibrary m_kernel32_handle;
  19. Common::DynamicLibrary m_api_ms_win_core_memory_l1_1_6_handle;
  20. void* m_address_UnmapViewOfFileEx = nullptr;
  21. void* m_address_VirtualAlloc2 = nullptr;
  22. void* m_address_MapViewOfFile3 = nullptr;
  23. };
  24. #endif
  25. // This class lets you create a block of anonymous RAM, and then arbitrarily map views into it.
  26. // Multiple views can mirror the same section of the block, which makes it very convenient for
  27. // emulating memory mirrors.
  28. class MemArena final
  29. {
  30. public:
  31. MemArena();
  32. ~MemArena();
  33. MemArena(const MemArena&) = delete;
  34. MemArena(MemArena&&) = delete;
  35. MemArena& operator=(const MemArena&) = delete;
  36. MemArena& operator=(MemArena&&) = delete;
  37. ///
  38. /// Allocate the singular memory segment handled by this MemArena. This will be the actual
  39. /// 'physical' available memory for this arena. After allocation, it can be interacted with using
  40. /// CreateView() and ReleaseView(). Used to make a mappable region for emulated memory.
  41. ///
  42. /// @param size The amount of bytes that should be allocated in this region.
  43. /// @param base_name A base name for the shared memory region, if applicable for this platform.
  44. /// Will be extended with the process ID.
  45. ///
  46. void GrabSHMSegment(size_t size, std::string_view base_name);
  47. ///
  48. /// Release the memory segment previously allocated with GrabSHMSegment().
  49. /// Should not be called before all views have been released.
  50. ///
  51. void ReleaseSHMSegment();
  52. ///
  53. /// Map a memory region in the memory segment previously allocated with GrabSHMSegment().
  54. ///
  55. /// @param offset Offset within the memory segment to map at.
  56. /// @param size Size of the region to map.
  57. ///
  58. /// @return Pointer to the memory region, or nullptr on failure.
  59. ///
  60. void* CreateView(s64 offset, size_t size);
  61. ///
  62. /// Unmap a memory region previously mapped with CreateView().
  63. /// Should not be called on a view that is still mapped into the virtual memory region.
  64. ///
  65. /// @param view Pointer returned by CreateView().
  66. /// @param size Size passed to the corresponding CreateView() call.
  67. ///
  68. void ReleaseView(void* view, size_t size);
  69. ///
  70. /// Reserve the singular 'virtual' memory region handled by this MemArena. This is used to create
  71. /// our 'fastmem' memory area for the emulated game code to access directly.
  72. ///
  73. /// @param memory_size Size in bytes of the memory region to reserve.
  74. ///
  75. /// @return Pointer to the memory region, or nullptr on failure.
  76. ///
  77. u8* ReserveMemoryRegion(size_t memory_size);
  78. ///
  79. /// Release the memory region previously reserved with ReserveMemoryRegion().
  80. /// Should not be called while any memory region is still mapped.
  81. ///
  82. void ReleaseMemoryRegion();
  83. ///
  84. /// Map a section from the memory segment previously allocated with GrabSHMSegment()
  85. /// into the region previously reserved with ReserveMemoryRegion().
  86. ///
  87. /// @param offset Offset within the memory segment previous allocated by GrabSHMSegment() to map
  88. /// from.
  89. /// @param size Size of the region to map.
  90. /// @param base Address within the memory region from ReserveMemoryRegion() where to map it.
  91. ///
  92. /// @return The address we actually ended up mapping, which should be the given 'base'.
  93. ///
  94. void* MapInMemoryRegion(s64 offset, size_t size, void* base);
  95. ///
  96. /// Unmap a memory region previously mapped with MapInMemoryRegion().
  97. ///
  98. /// @param view Pointer returned by MapInMemoryRegion().
  99. /// @param size Size passed to the corresponding MapInMemoryRegion() call.
  100. ///
  101. void UnmapFromMemoryRegion(void* view, size_t size);
  102. private:
  103. #ifdef _WIN32
  104. WindowsMemoryRegion* EnsureSplitRegionForMapping(void* address, size_t size);
  105. bool JoinRegionsAfterUnmap(void* address, size_t size);
  106. std::vector<WindowsMemoryRegion> m_regions;
  107. void* m_reserved_region = nullptr;
  108. void* m_memory_handle = nullptr;
  109. WindowsMemoryFunctions m_memory_functions;
  110. #elif defined(__APPLE__)
  111. vm_address_t m_shm_address = 0;
  112. vm_size_t m_shm_size = 0;
  113. mem_entry_name_port_t m_shm_entry = MACH_PORT_NULL;
  114. vm_address_t m_region_address = 0;
  115. vm_size_t m_region_size = 0;
  116. #else
  117. int m_shm_fd = 0;
  118. void* m_reserved_region = nullptr;
  119. std::size_t m_reserved_region_size = 0;
  120. #endif
  121. };
  122. // This class represents a single fixed-size memory region where the individual memory pages are
  123. // only actually allocated on first access. The memory will be zero on first access.
  124. class LazyMemoryRegion final
  125. {
  126. public:
  127. LazyMemoryRegion();
  128. ~LazyMemoryRegion();
  129. LazyMemoryRegion(const LazyMemoryRegion&) = delete;
  130. LazyMemoryRegion(LazyMemoryRegion&&) = delete;
  131. LazyMemoryRegion& operator=(const LazyMemoryRegion&) = delete;
  132. LazyMemoryRegion& operator=(LazyMemoryRegion&&) = delete;
  133. ///
  134. /// Reserve a memory region.
  135. ///
  136. /// @param size The size of the region.
  137. ///
  138. /// @return The address the region was mapped at. Returns nullptr on failure.
  139. ///
  140. void* Create(size_t size);
  141. ///
  142. /// Reset the memory region back to zero, throwing away any mapped pages.
  143. /// This can only be called after a successful call to Create().
  144. ///
  145. void Clear();
  146. ///
  147. /// Release the memory previously reserved with Create(). After this call the pointer that was
  148. /// returned by Create() will become invalid.
  149. ///
  150. void Release();
  151. ///
  152. /// Ensure that the memory page at the given byte offset from the start of the memory region is
  153. /// writable. We use this on Windows as a workaround to only actually commit pages as they are
  154. /// written to. On other OSes this does nothing.
  155. ///
  156. /// @param offset The offset into the memory region that should be made writable if it isn't.
  157. ///
  158. void EnsureMemoryPageWritable(size_t offset)
  159. {
  160. #ifdef _WIN32
  161. const size_t block_index = offset / BLOCK_SIZE;
  162. if (m_writable_block_handles[block_index] == nullptr)
  163. MakeMemoryBlockWritable(block_index);
  164. #endif
  165. }
  166. private:
  167. void* m_memory = nullptr;
  168. size_t m_size = 0;
  169. #ifdef _WIN32
  170. void* m_zero_block = nullptr;
  171. constexpr static size_t BLOCK_SIZE = 8 * 1024 * 1024; // size of allocated memory blocks
  172. WindowsMemoryFunctions m_memory_functions;
  173. std::vector<void*> m_writable_block_handles;
  174. void MakeMemoryBlockWritable(size_t offset);
  175. #endif
  176. };
  177. } // namespace Common