memory_manager.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <algorithm>
  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/memory_manager.h"
  12. #include "video_core/rasterizer_interface.h"
  13. #include "video_core/renderer_base.h"
  14. namespace Tegra {
  15. MemoryManager::MemoryManager(Core::System& system_)
  16. : system{system_}, page_table(page_table_size) {}
  17. MemoryManager::~MemoryManager() = default;
  18. void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
  19. rasterizer = rasterizer_;
  20. }
  21. GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
  22. u64 remaining_size{size};
  23. for (u64 offset{}; offset < size; offset += page_size) {
  24. if (remaining_size < page_size) {
  25. SetPageEntry(gpu_addr + offset, page_entry + offset, remaining_size);
  26. } else {
  27. SetPageEntry(gpu_addr + offset, page_entry + offset);
  28. }
  29. remaining_size -= page_size;
  30. }
  31. return gpu_addr;
  32. }
  33. GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) {
  34. const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
  35. if (it != map_ranges.end() && it->first == gpu_addr) {
  36. it->second = size;
  37. } else {
  38. map_ranges.insert(it, MapRange{gpu_addr, size});
  39. }
  40. return UpdateRange(gpu_addr, cpu_addr, size);
  41. }
  42. GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) {
  43. return Map(cpu_addr, *FindFreeRange(size, align), size);
  44. }
  45. GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) {
  46. const std::optional<GPUVAddr> gpu_addr = FindFreeRange(size, 1, true);
  47. ASSERT(gpu_addr);
  48. return Map(cpu_addr, *gpu_addr, size);
  49. }
  50. void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
  51. if (size == 0) {
  52. return;
  53. }
  54. const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
  55. if (it != map_ranges.end()) {
  56. ASSERT(it->first == gpu_addr);
  57. map_ranges.erase(it);
  58. } else {
  59. ASSERT_MSG(false, "Unmapping non-existent GPU address=0x{:x}", gpu_addr);
  60. }
  61. const auto submapped_ranges = GetSubmappedRange(gpu_addr, size);
  62. for (const auto& [map_addr, map_size] : submapped_ranges) {
  63. // Flush and invalidate through the GPU interface, to be asynchronous if possible.
  64. const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map_addr);
  65. ASSERT(cpu_addr);
  66. rasterizer->UnmapMemory(*cpu_addr, map_size);
  67. }
  68. UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
  69. }
  70. std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) {
  71. for (u64 offset{}; offset < size; offset += page_size) {
  72. if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) {
  73. return std::nullopt;
  74. }
  75. }
  76. return UpdateRange(gpu_addr, PageEntry::State::Allocated, size);
  77. }
  78. GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) {
  79. return *AllocateFixed(*FindFreeRange(size, align), size);
  80. }
  81. void MemoryManager::TryLockPage(PageEntry page_entry, std::size_t size) {
  82. if (!page_entry.IsValid()) {
  83. return;
  84. }
  85. ASSERT(system.CurrentProcess()
  86. ->PageTable()
  87. .LockForDeviceAddressSpace(page_entry.ToAddress(), size)
  88. .IsSuccess());
  89. }
  90. void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) {
  91. if (!page_entry.IsValid()) {
  92. return;
  93. }
  94. ASSERT(system.CurrentProcess()
  95. ->PageTable()
  96. .UnlockForDeviceAddressSpace(page_entry.ToAddress(), size)
  97. .IsSuccess());
  98. }
  99. PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const {
  100. return page_table[PageEntryIndex(gpu_addr)];
  101. }
  102. void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
  103. // TODO(bunnei): We should lock/unlock device regions. This currently causes issues due to
  104. // improper tracking, but should be fixed in the future.
  105. //// Unlock the old page
  106. // TryUnlockPage(page_table[PageEntryIndex(gpu_addr)], size);
  107. //// Lock the new page
  108. // TryLockPage(page_entry, size);
  109. auto& current_page = page_table[PageEntryIndex(gpu_addr)];
  110. if ((!current_page.IsValid() && page_entry.IsValid()) ||
  111. current_page.ToAddress() != page_entry.ToAddress()) {
  112. rasterizer->ModifyGPUMemory(gpu_addr, size);
  113. }
  114. current_page = page_entry;
  115. }
  116. std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align,
  117. bool start_32bit_address) const {
  118. if (!align) {
  119. align = page_size;
  120. } else {
  121. align = Common::AlignUp(align, page_size);
  122. }
  123. u64 available_size{};
  124. GPUVAddr gpu_addr{start_32bit_address ? address_space_start_low : address_space_start};
  125. while (gpu_addr + available_size < address_space_size) {
  126. if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) {
  127. available_size += page_size;
  128. if (available_size >= size) {
  129. return gpu_addr;
  130. }
  131. } else {
  132. gpu_addr += available_size + page_size;
  133. available_size = 0;
  134. const auto remainder{gpu_addr % align};
  135. if (remainder) {
  136. gpu_addr = (gpu_addr - remainder) + align;
  137. }
  138. }
  139. }
  140. return std::nullopt;
  141. }
  142. std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
  143. if (gpu_addr == 0) {
  144. return std::nullopt;
  145. }
  146. const auto page_entry{GetPageEntry(gpu_addr)};
  147. if (!page_entry.IsValid()) {
  148. return std::nullopt;
  149. }
  150. return page_entry.ToAddress() + (gpu_addr & page_mask);
  151. }
  152. std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const {
  153. size_t page_index{addr >> page_bits};
  154. const size_t page_last{(addr + size + page_size - 1) >> page_bits};
  155. while (page_index < page_last) {
  156. const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
  157. if (page_addr && *page_addr != 0) {
  158. return page_addr;
  159. }
  160. ++page_index;
  161. }
  162. return std::nullopt;
  163. }
  164. template <typename T>
  165. T MemoryManager::Read(GPUVAddr addr) const {
  166. if (auto page_pointer{GetPointer(addr)}; page_pointer) {
  167. // NOTE: Avoid adding any extra logic to this fast-path block
  168. T value;
  169. std::memcpy(&value, page_pointer, sizeof(T));
  170. return value;
  171. }
  172. ASSERT(false);
  173. return {};
  174. }
  175. template <typename T>
  176. void MemoryManager::Write(GPUVAddr addr, T data) {
  177. if (auto page_pointer{GetPointer(addr)}; page_pointer) {
  178. // NOTE: Avoid adding any extra logic to this fast-path block
  179. std::memcpy(page_pointer, &data, sizeof(T));
  180. return;
  181. }
  182. ASSERT(false);
  183. }
  184. template u8 MemoryManager::Read<u8>(GPUVAddr addr) const;
  185. template u16 MemoryManager::Read<u16>(GPUVAddr addr) const;
  186. template u32 MemoryManager::Read<u32>(GPUVAddr addr) const;
  187. template u64 MemoryManager::Read<u64>(GPUVAddr addr) const;
  188. template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data);
  189. template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
  190. template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
  191. template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data);
  192. u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) {
  193. if (!GetPageEntry(gpu_addr).IsValid()) {
  194. return {};
  195. }
  196. const auto address{GpuToCpuAddress(gpu_addr)};
  197. if (!address) {
  198. return {};
  199. }
  200. return system.Memory().GetPointer(*address);
  201. }
  202. const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
  203. if (!GetPageEntry(gpu_addr).IsValid()) {
  204. return {};
  205. }
  206. const auto address{GpuToCpuAddress(gpu_addr)};
  207. if (!address) {
  208. return {};
  209. }
  210. return system.Memory().GetPointer(*address);
  211. }
  212. size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept {
  213. auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first);
  214. --it;
  215. return it->second - (gpu_addr - it->first);
  216. }
  217. void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
  218. bool is_safe) const {
  219. std::size_t remaining_size{size};
  220. std::size_t page_index{gpu_src_addr >> page_bits};
  221. std::size_t page_offset{gpu_src_addr & page_mask};
  222. while (remaining_size > 0) {
  223. const std::size_t copy_amount{
  224. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  225. const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
  226. if (page_addr && *page_addr != 0) {
  227. const auto src_addr{*page_addr + page_offset};
  228. if (is_safe) {
  229. // Flush must happen on the rasterizer interface, such that memory is always
  230. // synchronous when it is read (even when in asynchronous GPU mode).
  231. // Fixes Dead Cells title menu.
  232. rasterizer->FlushRegion(src_addr, copy_amount);
  233. }
  234. system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
  235. } else {
  236. std::memset(dest_buffer, 0, copy_amount);
  237. }
  238. page_index++;
  239. page_offset = 0;
  240. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  241. remaining_size -= copy_amount;
  242. }
  243. }
  244. void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
  245. ReadBlockImpl(gpu_src_addr, dest_buffer, size, true);
  246. }
  247. void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
  248. const std::size_t size) const {
  249. ReadBlockImpl(gpu_src_addr, dest_buffer, size, false);
  250. }
  251. void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
  252. bool is_safe) {
  253. std::size_t remaining_size{size};
  254. std::size_t page_index{gpu_dest_addr >> page_bits};
  255. std::size_t page_offset{gpu_dest_addr & page_mask};
  256. while (remaining_size > 0) {
  257. const std::size_t copy_amount{
  258. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  259. const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
  260. if (page_addr && *page_addr != 0) {
  261. const auto dest_addr{*page_addr + page_offset};
  262. if (is_safe) {
  263. // Invalidate must happen on the rasterizer interface, such that memory is always
  264. // synchronous when it is written (even when in asynchronous GPU mode).
  265. rasterizer->InvalidateRegion(dest_addr, copy_amount);
  266. }
  267. system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
  268. }
  269. page_index++;
  270. page_offset = 0;
  271. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  272. remaining_size -= copy_amount;
  273. }
  274. }
  275. void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
  276. WriteBlockImpl(gpu_dest_addr, src_buffer, size, true);
  277. }
  278. void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
  279. std::size_t size) {
  280. WriteBlockImpl(gpu_dest_addr, src_buffer, size, false);
  281. }
  282. void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
  283. size_t remaining_size{size};
  284. size_t page_index{gpu_addr >> page_bits};
  285. size_t page_offset{gpu_addr & page_mask};
  286. while (remaining_size > 0) {
  287. const size_t num_bytes{std::min(page_size - page_offset, remaining_size)};
  288. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  289. rasterizer->FlushRegion(*page_addr + page_offset, num_bytes);
  290. }
  291. ++page_index;
  292. page_offset = 0;
  293. remaining_size -= num_bytes;
  294. }
  295. }
  296. void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) {
  297. std::vector<u8> tmp_buffer(size);
  298. ReadBlock(gpu_src_addr, tmp_buffer.data(), size);
  299. // The output block must be flushed in case it has data modified from the GPU.
  300. // Fixes NPC geometry in Zombie Panic in Wonderland DX
  301. FlushRegion(gpu_dest_addr, size);
  302. WriteBlock(gpu_dest_addr, tmp_buffer.data(), size);
  303. }
  304. bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
  305. const auto cpu_addr{GpuToCpuAddress(gpu_addr)};
  306. if (!cpu_addr) {
  307. return false;
  308. }
  309. const std::size_t page{(*cpu_addr & Core::Memory::YUZU_PAGEMASK) + size};
  310. return page <= Core::Memory::YUZU_PAGESIZE;
  311. }
  312. bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const {
  313. size_t page_index{gpu_addr >> page_bits};
  314. const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits};
  315. std::optional<VAddr> old_page_addr{};
  316. while (page_index != page_last) {
  317. const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
  318. if (!page_addr || *page_addr == 0) {
  319. return false;
  320. }
  321. if (old_page_addr) {
  322. if (*old_page_addr + page_size != *page_addr) {
  323. return false;
  324. }
  325. }
  326. old_page_addr = page_addr;
  327. ++page_index;
  328. }
  329. return true;
  330. }
  331. bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const {
  332. size_t page_index{gpu_addr >> page_bits};
  333. const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits};
  334. while (page_index < page_last) {
  335. if (!page_table[page_index].IsValid() || page_table[page_index].ToAddress() == 0) {
  336. return false;
  337. }
  338. ++page_index;
  339. }
  340. return true;
  341. }
  342. std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
  343. GPUVAddr gpu_addr, std::size_t size) const {
  344. std::vector<std::pair<GPUVAddr, std::size_t>> result{};
  345. size_t page_index{gpu_addr >> page_bits};
  346. size_t remaining_size{size};
  347. size_t page_offset{gpu_addr & page_mask};
  348. std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{};
  349. std::optional<VAddr> old_page_addr{};
  350. const auto extend_size = [&last_segment, &page_index, &page_offset](std::size_t bytes) {
  351. if (!last_segment) {
  352. const GPUVAddr new_base_addr = (page_index << page_bits) + page_offset;
  353. last_segment = {new_base_addr, bytes};
  354. } else {
  355. last_segment->second += bytes;
  356. }
  357. };
  358. const auto split = [&last_segment, &result] {
  359. if (last_segment) {
  360. result.push_back(*last_segment);
  361. last_segment = std::nullopt;
  362. }
  363. };
  364. while (remaining_size > 0) {
  365. const size_t num_bytes{std::min(page_size - page_offset, remaining_size)};
  366. const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
  367. if (!page_addr || *page_addr == 0) {
  368. split();
  369. } else if (old_page_addr) {
  370. if (*old_page_addr + page_size != *page_addr) {
  371. split();
  372. }
  373. extend_size(num_bytes);
  374. } else {
  375. extend_size(num_bytes);
  376. }
  377. ++page_index;
  378. page_offset = 0;
  379. remaining_size -= num_bytes;
  380. old_page_addr = page_addr;
  381. }
  382. split();
  383. return result;
  384. }
  385. } // namespace Tegra