memory_manager.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/alignment.h"
  5. #include "common/assert.h"
  6. #include "common/logging/log.h"
  7. #include "core/core.h"
  8. #include "core/hle/kernel/k_page_table.h"
  9. #include "core/hle/kernel/k_process.h"
  10. #include "core/memory.h"
  11. #include "video_core/gpu.h"
  12. #include "video_core/memory_manager.h"
  13. #include "video_core/rasterizer_interface.h"
  14. #include "video_core/renderer_base.h"
  15. namespace Tegra {
  16. MemoryManager::MemoryManager(Core::System& system_)
  17. : system{system_}, page_table(page_table_size) {}
  18. MemoryManager::~MemoryManager() = default;
  19. void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
  20. rasterizer = rasterizer_;
  21. }
  22. GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
  23. u64 remaining_size{size};
  24. for (u64 offset{}; offset < size; offset += page_size) {
  25. if (remaining_size < page_size) {
  26. SetPageEntry(gpu_addr + offset, page_entry + offset, remaining_size);
  27. } else {
  28. SetPageEntry(gpu_addr + offset, page_entry + offset);
  29. }
  30. remaining_size -= page_size;
  31. }
  32. return gpu_addr;
  33. }
  34. GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) {
  35. const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
  36. if (it != map_ranges.end() && it->first == gpu_addr) {
  37. it->second = size;
  38. } else {
  39. map_ranges.insert(it, MapRange{gpu_addr, size});
  40. }
  41. return UpdateRange(gpu_addr, cpu_addr, size);
  42. }
  43. GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) {
  44. return Map(cpu_addr, *FindFreeRange(size, align), size);
  45. }
  46. GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) {
  47. const std::optional<GPUVAddr> gpu_addr = FindFreeRange(size, 1, true);
  48. ASSERT(gpu_addr);
  49. return Map(cpu_addr, *gpu_addr, size);
  50. }
  51. void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
  52. if (size == 0) {
  53. return;
  54. }
  55. const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
  56. if (it != map_ranges.end()) {
  57. ASSERT(it->first == gpu_addr);
  58. map_ranges.erase(it);
  59. } else {
  60. UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr);
  61. }
  62. // Flush and invalidate through the GPU interface, to be asynchronous if possible.
  63. const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr);
  64. ASSERT(cpu_addr);
  65. rasterizer->UnmapMemory(*cpu_addr, size);
  66. UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
  67. }
  68. std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) {
  69. for (u64 offset{}; offset < size; offset += page_size) {
  70. if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) {
  71. return std::nullopt;
  72. }
  73. }
  74. return UpdateRange(gpu_addr, PageEntry::State::Allocated, size);
  75. }
  76. GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) {
  77. return *AllocateFixed(*FindFreeRange(size, align), size);
  78. }
  79. void MemoryManager::TryLockPage(PageEntry page_entry, std::size_t size) {
  80. if (!page_entry.IsValid()) {
  81. return;
  82. }
  83. ASSERT(system.CurrentProcess()
  84. ->PageTable()
  85. .LockForDeviceAddressSpace(page_entry.ToAddress(), size)
  86. .IsSuccess());
  87. }
  88. void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) {
  89. if (!page_entry.IsValid()) {
  90. return;
  91. }
  92. ASSERT(system.CurrentProcess()
  93. ->PageTable()
  94. .UnlockForDeviceAddressSpace(page_entry.ToAddress(), size)
  95. .IsSuccess());
  96. }
  97. PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const {
  98. return page_table[PageEntryIndex(gpu_addr)];
  99. }
  100. void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
  101. // TODO(bunnei): We should lock/unlock device regions. This currently causes issues due to
  102. // improper tracking, but should be fixed in the future.
  103. //// Unlock the old page
  104. // TryUnlockPage(page_table[PageEntryIndex(gpu_addr)], size);
  105. //// Lock the new page
  106. // TryLockPage(page_entry, size);
  107. page_table[PageEntryIndex(gpu_addr)] = page_entry;
  108. }
  109. std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align,
  110. bool start_32bit_address) const {
  111. if (!align) {
  112. align = page_size;
  113. } else {
  114. align = Common::AlignUp(align, page_size);
  115. }
  116. u64 available_size{};
  117. GPUVAddr gpu_addr{start_32bit_address ? address_space_start_low : address_space_start};
  118. while (gpu_addr + available_size < address_space_size) {
  119. if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) {
  120. available_size += page_size;
  121. if (available_size >= size) {
  122. return gpu_addr;
  123. }
  124. } else {
  125. gpu_addr += available_size + page_size;
  126. available_size = 0;
  127. const auto remainder{gpu_addr % align};
  128. if (remainder) {
  129. gpu_addr = (gpu_addr - remainder) + align;
  130. }
  131. }
  132. }
  133. return std::nullopt;
  134. }
  135. std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
  136. if (gpu_addr == 0) {
  137. return std::nullopt;
  138. }
  139. const auto page_entry{GetPageEntry(gpu_addr)};
  140. if (!page_entry.IsValid()) {
  141. return std::nullopt;
  142. }
  143. return page_entry.ToAddress() + (gpu_addr & page_mask);
  144. }
  145. template <typename T>
  146. T MemoryManager::Read(GPUVAddr addr) const {
  147. if (auto page_pointer{GetPointer(addr)}; page_pointer) {
  148. // NOTE: Avoid adding any extra logic to this fast-path block
  149. T value;
  150. std::memcpy(&value, page_pointer, sizeof(T));
  151. return value;
  152. }
  153. UNREACHABLE();
  154. return {};
  155. }
  156. template <typename T>
  157. void MemoryManager::Write(GPUVAddr addr, T data) {
  158. if (auto page_pointer{GetPointer(addr)}; page_pointer) {
  159. // NOTE: Avoid adding any extra logic to this fast-path block
  160. std::memcpy(page_pointer, &data, sizeof(T));
  161. return;
  162. }
  163. UNREACHABLE();
  164. }
  165. template u8 MemoryManager::Read<u8>(GPUVAddr addr) const;
  166. template u16 MemoryManager::Read<u16>(GPUVAddr addr) const;
  167. template u32 MemoryManager::Read<u32>(GPUVAddr addr) const;
  168. template u64 MemoryManager::Read<u64>(GPUVAddr addr) const;
  169. template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data);
  170. template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
  171. template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
  172. template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data);
  173. u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) {
  174. if (!GetPageEntry(gpu_addr).IsValid()) {
  175. return {};
  176. }
  177. const auto address{GpuToCpuAddress(gpu_addr)};
  178. if (!address) {
  179. return {};
  180. }
  181. return system.Memory().GetPointer(*address);
  182. }
  183. const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
  184. if (!GetPageEntry(gpu_addr).IsValid()) {
  185. return {};
  186. }
  187. const auto address{GpuToCpuAddress(gpu_addr)};
  188. if (!address) {
  189. return {};
  190. }
  191. return system.Memory().GetPointer(*address);
  192. }
  193. size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept {
  194. auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first);
  195. --it;
  196. return it->second - (gpu_addr - it->first);
  197. }
  198. void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
  199. std::size_t remaining_size{size};
  200. std::size_t page_index{gpu_src_addr >> page_bits};
  201. std::size_t page_offset{gpu_src_addr & page_mask};
  202. while (remaining_size > 0) {
  203. const std::size_t copy_amount{
  204. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  205. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  206. const auto src_addr{*page_addr + page_offset};
  207. // Flush must happen on the rasterizer interface, such that memory is always synchronous
  208. // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
  209. rasterizer->FlushRegion(src_addr, copy_amount);
  210. system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
  211. }
  212. page_index++;
  213. page_offset = 0;
  214. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  215. remaining_size -= copy_amount;
  216. }
  217. }
  218. void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
  219. const std::size_t size) const {
  220. std::size_t remaining_size{size};
  221. std::size_t page_index{gpu_src_addr >> page_bits};
  222. std::size_t page_offset{gpu_src_addr & page_mask};
  223. while (remaining_size > 0) {
  224. const std::size_t copy_amount{
  225. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  226. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  227. const auto src_addr{*page_addr + page_offset};
  228. system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
  229. } else {
  230. std::memset(dest_buffer, 0, copy_amount);
  231. }
  232. page_index++;
  233. page_offset = 0;
  234. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  235. remaining_size -= copy_amount;
  236. }
  237. }
  238. void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
  239. std::size_t remaining_size{size};
  240. std::size_t page_index{gpu_dest_addr >> page_bits};
  241. std::size_t page_offset{gpu_dest_addr & page_mask};
  242. while (remaining_size > 0) {
  243. const std::size_t copy_amount{
  244. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  245. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  246. const auto dest_addr{*page_addr + page_offset};
  247. // Invalidate must happen on the rasterizer interface, such that memory is always
  248. // synchronous when it is written (even when in asynchronous GPU mode).
  249. rasterizer->InvalidateRegion(dest_addr, copy_amount);
  250. system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
  251. }
  252. page_index++;
  253. page_offset = 0;
  254. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  255. remaining_size -= copy_amount;
  256. }
  257. }
  258. void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
  259. std::size_t size) {
  260. std::size_t remaining_size{size};
  261. std::size_t page_index{gpu_dest_addr >> page_bits};
  262. std::size_t page_offset{gpu_dest_addr & page_mask};
  263. while (remaining_size > 0) {
  264. const std::size_t copy_amount{
  265. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  266. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  267. const auto dest_addr{*page_addr + page_offset};
  268. system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
  269. }
  270. page_index++;
  271. page_offset = 0;
  272. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  273. remaining_size -= copy_amount;
  274. }
  275. }
  276. void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
  277. size_t remaining_size{size};
  278. size_t page_index{gpu_addr >> page_bits};
  279. size_t page_offset{gpu_addr & page_mask};
  280. while (remaining_size > 0) {
  281. const size_t num_bytes{std::min(page_size - page_offset, remaining_size)};
  282. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  283. rasterizer->FlushRegion(*page_addr + page_offset, num_bytes);
  284. }
  285. ++page_index;
  286. page_offset = 0;
  287. remaining_size -= num_bytes;
  288. }
  289. }
  290. void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) {
  291. std::vector<u8> tmp_buffer(size);
  292. ReadBlock(gpu_src_addr, tmp_buffer.data(), size);
  293. // The output block must be flushed in case it has data modified from the GPU.
  294. // Fixes NPC geometry in Zombie Panic in Wonderland DX
  295. FlushRegion(gpu_dest_addr, size);
  296. WriteBlock(gpu_dest_addr, tmp_buffer.data(), size);
  297. }
  298. bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
  299. const auto cpu_addr{GpuToCpuAddress(gpu_addr)};
  300. if (!cpu_addr) {
  301. return false;
  302. }
  303. const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size};
  304. return page <= Core::Memory::PAGE_SIZE;
  305. }
  306. } // namespace Tegra